GA4 Items Data for Non-Ecommerce Events
2025-02-06
By: Linda Eriksson
Digital Analyst

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?
- 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. - Formats product data into a string
Each attribute, such asitem_id
,item_name
, andprice
, is separated by|
. If multiple products exist, they are separated by,
. - 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. - 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:
- Unnesting the event parameter to access the items string (
items_to_string
) - Converting the string into an items array that matches GA4’s schema.
- 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:
- Unnest the Event Parameter
First, the event parameter is unnested to access the string containing the product data, specifically the parameteritems_to_string
that is sent with theproduct_in_store_check
event. The string is then split using theSPLIT
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.
- 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 theSPLIT
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 assignedCAST(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.
- Combine with Other Items Data from E-Commerce Events
The processed results fromparsed_items
(whereproduct_in_store_check
is handled) are combined with data fromecommerce_items
, which handles standardized e-commerce events such asview_item
,add_to_cart
, andpurchase
. 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.