GA4 Items Data for Non-Ecommerce Events

2025-02-06

By: Linda Eriksson

Digital Analyst

Feature image

Part 1 - Background

When working with e-commerce tracking, it’s common to send product data to Google Analytics using their predefined e-commerce events such as view_item, add_to_cart, and purchase, among others. But what happens when you need to include product data in an event that isn’t tied to Google’s predefined events - i.e., a custom event? You will quickly run into a limitation: Google Analytics 4 does not support this.

Examples of When Product Data is Needed in Custom Events

There are several situations where it might be crucial to include product data in custom events. Here are some examples:

  • Is the product available in-store?
    Measure how often users check product availability in physical stores.
  • Notify me when the product is back in stock
    When a user signs up for back-in-stock notifications.
  • Share product
    Measure product sharing via social media or email.

Why Doesn’t the Items Array Work in Custom Events?

Google Analytics 4 is designed to handle e-commerce data through predefined events. These events have built-in support for an items array, making it easy to send product data and have it visualized in predefined e-commerce reports. However, custom events do not work in the same way. If you try to include an items array in a custom event, it is filtered out or ignored entirely by GA4. The problem lies in GA4’s lack of mechanisms to interpret and process items array in custom events. The underlying gtag.js script used to send data to GA4 is built with rules that only allow items to be included in standard e-commerce events. If a custom event contains an items array, the script does not recognize it as a valid parameter and filters it out before the request is even sent.

The Solution: Combine JavaScript in GTM and SQL in BigQuery

One option is to use a predefined e-commerce event for other types of interactions and ignore the fact that the event name doesn’t reflect the actual action. However, this approach is risky as it can lead to misunderstandings within the organization and misinterpretation of data. A better solution is to create a custom event via GTM and send the product information as a string in an event parameter, then perform your analysis in BigQuery. This way, the product information is preserved without being filtered out by GA4. The advantage of this solution becomes clear when the data is exported to BigQuery. Here, you can use SQL to split the converted string into separate columns, providing the same level of detail as an items array. This allows for deeper analysis while maintaining clarity and structure similar to the rest of the items array.

Part 2 - Configuring in GTM

Create a Custom JavaScript Variable

With a custom JavaScript variable, you can transform your items into a comma-separated string. This function is flexible and can be adjusted based on which parts of the product information you want to include.

What Does the JavaScript Function Do, and Why?

  1. Gets the latest event with product data (items)
    The function ensures it works with the latest product information in the dataLayer, even if multiple events have already been pushed.

  2. Formats product data into a string
    Each attribute, such as item_id, item_name, and price, is separated by |. If multiple products exist, they are separated by ,.

  3. Maintains a consistent structure
    Data is formatted in the same order regardless of how it is received. Maintaining consistent value order is critical for ensuring data quality.

  4. Handles missing values
    If an attribute is missing, it is replaced with an empty string to preserve the format.

The Javascript code

This is the JavaScript function used within a variable in GTM. In the example below, the variable is named cJS - items to string.

function() {
 // Check if dataLayer exists
 if (!window.dataLayer) {
 console.warn("dataLayer is not defined");
 return "";
 }
 // Get the latest 'items' array directly from dataLayer.ecommerce.items
 var latestDataLayerEntry = window.dataLayer[window.dataLayer.length - 1];
 var latestEcommerce = latestDataLayerEntry && latestDataLayerEntry.ecommerce;
 if (!latestEcommerce || !latestEcommerce.items) {
 console.warn("No 'ecommerce' event with 'items' found in dataLayer");
 return "";
 }
 // Retrieve the items array
 var items = latestEcommerce.items;
 // Check that items is an array
 if (!Array.isArray(items)) {
 console.warn("'items' is not an array");
 return "";
 }
 // Define the order of attributes to include in the string
 var attributeOrder = ["item_id", "item_name", "item_brand", "item_category", "price"];
 // Iterate over items and build the string based on the defined order
 var formattedItems = items.map(function(item) {
 return attributeOrder.map(function(attr) {
 return item[attr] !== undefined ? item[attr] : ""; // If the attribute is missing, use an empty string
 }).join("|"); // Separate attributes for each item with "|"
 });
 // Combine all items into a single string, separated by ","
 return formattedItems.join(",");
}

Use the Variable in Your Event Tag

Add the created JavaScript variable as an event parameter in your GA4 event tag. In the example, the parameter is named items_to_string. After this, validate that the event parameter is correctly sent in the outgoing GA4 request.


The screenshot shows the GA4 tag in GTM for the event product_in_store_check, with the event parameter items_to_string.


The screenshot shows that the transformation is correct. We have an outgoing request to GA4 where the items array has been converted into a string that is sent in an event parameter called items_to_string.

Part 3 - Query Data in BigQuery

When the event data is being streamed to BigQuery, the next step is to structure it for analysis. This involves:

  1. Unnesting the event parameter to access the items string (items_to_string)
  2. Converting the string into an items array that matches GA4’s schema.
  3. Combining custom data with other e-commerce data via a UNION ALL. The result is a unified dataset where custom events and standard e-commerce events are organized in the same structure, making it easy to analyze product lifecycles from a consolidated perspective.

The screenshot shows product_in_store_check with its associated event parameter being sent to BigQuery as a string. The product information is located in the columns event_params.key and event_params.value.


The screenshot shows the final result where the custom event and standard e-commerce events share a unified structure, with all product information consolidated under the same format in the GA4 schema’s items columns.

The SQL query

Below is the query along with an explanation of its structure. The purpose is to demonstrate a concept that can be easily developed further for use in analysis. This involves:

  1. Unnest the Event Parameter
    First, the event parameter is unnested to access the string containing the product data, specifically the parameter items_to_string that is sent with the product_in_store_check event. The string is then split using the SPLIT function, based on commas. Each part of the resulting list corresponds to an individual product. This method accounts for the possibility that an event may include multiple products.

  2. Convert the Item String
    In the next step, each part of the string is transformed into an item array that matches the GA4 export schema. This is done by splitting the string using the SPLIT function, based on the | character, which is used to separate values in the string. Fields that are not present in the string but are required to match the GA4 schema are assigned CAST(NULL AS ...). This is useful to ensure that fields in tables or queries always have the correct data type, even if they contain no value.

  3. Combine with Other Items Data from E-Commerce Events
    The processed results from parsed_items (where product_in_store_check is handled) are combined with data from ecommerce_items, which handles standardized e-commerce events such as view_item, add_to_cart, and purchase. This combination allows for the analysis of product data from both custom events and standardized events within the same dataset.

WITH parsed_items AS (
 SELECT
 ARRAY(
 SELECT AS STRUCT
 SPLIT(item_params, '|')[SAFE_OFFSET(0)] AS item_id,
 SPLIT(item_params, '|')[SAFE_OFFSET(1)] AS item_name,
 SPLIT(item_params, '|')[SAFE_OFFSET(2)] AS item_brand,
 SPLIT(item_params, '|')[SAFE_OFFSET(3)] AS item_category,
 CAST(NULL AS STRING) AS item_category2,
 CAST(NULL AS STRING) AS item_category3,
 CAST(NULL AS STRING) AS item_category4,
 CAST(NULL AS STRING) AS item_category5,
 CAST(REPLACE(SPLIT(item_params, '|')[SAFE_OFFSET(4)], ',', '.') AS FLOAT64) AS price,
 CAST(NULL AS INT) AS quantity,
 CAST(NULL AS STRING) AS coupon,
 CAST(NULL AS STRING) AS affiliation,
 CAST(NULL AS STRING) AS location_id,
 CAST(NULL AS STRING) AS item_list_id,
 CAST(NULL AS STRING) AS item_list_name,
 CAST(NULL AS STRING) AS item_list_index,
 CAST(NULL AS STRING) AS promotion_id,
 CAST(NULL AS STRING) AS promotion_name,
 CAST(NULL AS STRING) AS creative_name,
 CAST(NULL AS STRING) AS creative_slot
 FROM
 UNNEST(SPLIT(
 (SELECT ep.value.string_value
 FROM UNNEST(event_params) AS ep
 WHERE ep.key = 'items_to_string'
 ),
 ','
 )) AS item_params
 ) AS items
 FROM `example_project.analytics.events_*`
 WHERE event_name = "product_in_store_check"
),
ecommerce_items AS (
 SELECT
 ARRAY(
 SELECT AS STRUCT
 item_id,
 item_name,
 item_brand,
 item_category,
 item_category2,
 item_category3,
 item_category4,
 item_category5,
 price,
 quantity,
 coupon,
 affiliation,
 location_id,
 item_list_id,
 item_list_name,
 item_list_index,
 promotion_id,
 promotion_name,
 creative_name,
 creative_slot
 FROM UNNEST(items)
 ) AS items
 FROM `example_project.analytics.events_*`
 WHERE event_name IN ("purchase", "add_to_cart", "view_item")
)
SELECT * FROM parsed_items
UNION ALL
SELECT * FROM ecommerce_items

Summary

Including product data in custom events in Google Analytics 4 (GA4) is a well-known limitation that can complicate both tracking and analysis. However, by exporting data to BigQuery, you can bypass this limitation and work directly with raw data, enabling flexible solutions like the one presented in this post.

Additionally, using tools like Dataform allows for the automation of product data mapping before it is sent to your analyses. This means analysts can avoid spending time on complex queries and instead work with product data in a more standardized and efficient way.

If you’re interested in starting your journey with querying data in BigQuery, I highly recommend reading our post Beginner’s Guide to Working with GA4 Data in BigQuery. If you’re interested in learning more about utilizing Dataform, be sure to check this out: Getting Started with Dataform.