Here’s a typical problem in Shopify and Woocommerce configurations: in Google Tag Manager, you notice double datalayer pushes of ecommerce events, specifically “view_item”, “add_to_cart”, and “purchase” events. This pollutes your data, and it’s caused by the simultaneous presence of one app or plugin pushing GA4 DataLayer events, and another one pushing Google Shopping DataLayer events.
Here’s how to fix it.
Table of Contents
The double DataLayer pushes of ecommerce events
We have noticed this in many Shopify installations, but also often in Woocommerce. Let’s focus on the latter now, but the same approach applies to Shopify.
You navigate your preview mode of GTM and you see something like this on a product page (for the scope of this debug, we’re going to focus only on the view_item event).
When you inspect the duplicated view_item event, you notice these two different syntaxes.
dataLayer.push({
ecommerce: {
currency: "EUR",
value: 250,
items: [
{
item_id: "XXX",
item_name: "Product Name",
sku: "XXX",
price: 250,
stocklevel: 9,
stockstatus: "instock",
google_business_vertical: "retail",
item_category: "Category Name",
id: "XXX"
}
]
},
event: "view_item",
gtm.uniqueEventId: 9
})
The other view_item event, has this syntax
dataLayer.push({
event: "view_item",
eventModel: {
ecomm_pagetype: "product",
value: 250,
items: [
{
id: "XXX",
price: 250,
google_business_vertical: "retail",
name: "Product Name",
category: "Category Name" +
"amp; Category Name amp" +
"; Category Name"
}
]
},
gtm.uniqueEventId: 8
})
How to solve the double datalayer pushes of ecommerce hits
At this point you typically have three options.
- Option 1: uninstall the plugin or app that is pushing the double datalayer pushes of the event, and rely on an alternative method to sync your shopping feed (API, manual upload, another app..)
- Option 2: filter your GTM events with a variable lookup (what we will do here now)
- Option 3: hire tracking experts like Fuel LAB’s team, to have your own custom DataLayer pushes.
Obviously option 3 offers the best solution, as you’ll be able to send data to all the relevant platforms in the right syntax, but also Option 2 is a viable option.
So here’s how to filter the double datalayer push of your ecommerce events for your GA4 Triggers.
Filtering your ecommerce tracking triggers
The goal is for your ecommerce events triggers to ignore the duplicated push that contains the syntax based on “eventModel”, while firing only when the syntax doesn’t contain “eventModel”.
The way to achieve this depends on your current Ecommerce Tracking. On our end, we have the following implementation for tracking ecommerce events.
Ecommerce Tags configuration example
Let’s say that these are all the ecommerce events that you want to track in GA4:
- menu_click
- view_promotion
- view_category
- select_promotion
- view_item
- view_item_list
- select_item
- add_to_cart
- remove_from_cart
- view_cart
- begin_checkout
- add_payment_info
- add_shipping_info
- purchase
It would be really messy to make one trigger per each event, so it’s likely you’ve configured your trigger this way:
Trigger Configuration: Custom Event
Event (with regex matching checked): view_promotion|view_category|select_promotion|view_item|view_item_list|select_item|add_to_cart|remove_from_cart|view_cart|begin_checkout|add_payment_info|add_shipping_info|purchase
Then, most likely, you have one GA4 Event tag that sends the triggering event with all the custom paramters using the DataLayer information, to GA4.
Filtering with a Boolean Javascript
The most obvious way to try and fix the problem, is to set a condition to the Ecommerce Trigger so that it will fire when it reads one of your events, but only in specific cases:
You can use a custom javascript variable to look for the presence of the object “eventModel”; if it’s found, it should output “true”; if not found, it should output “false”. Here’s an example javascript:
function() {
var isEventModelPresent = false;
if (typeof dataLayer !== 'undefined') {
var dataLayerString = JSON.stringify(dataLayer);
if (dataLayerString.indexOf('eventModel') !== -1) {
isEventModelPresent = true;
}
}
return isEventModelPresent;
}
But, depending on your implementation and how soon the DataLayer is populated compared to when the variable runs the script, this approach might be unreliable. I will update this post in the future after further investigation.
Filtering with Data Layer Variable lookup
Another and tested approach is to look for the “undefined” value of the first key in the eventModel object. Infact, if Google Tag Manager finds the key, it will render the content of the key, but if not, it will return “undefined”. You can leverage this behavior to stop your trigger from firing.
Here’s how:
Step 1:
Create the variable “eventModel”.
Step 2:
Create the Javascript that will read the content of eventModel:
function() {
var eventModel = {{eventModel}};
if (eventModel) {
return eventModel;
} else {
return undefined;
}
}
Step 3:
Now access the first level of your DataLayer push. In this case, it was “eventModel.ecomm_pagetype“
Step 4:
Now that we can read the content of ecomm_pagetype, we can hunt down that “undefined” response when the eventModel object is not present at all.
Conclusions
As a result, your ecommerce events are going to fire the tag associating with this redesigned trigger, only when the syntax of the DataLayer is the standard one (or the one of your choice). Hope this helps.
Pietro Mingotti is an Italian entrepreneur and digital marketing specialist, best known as the founder and owner of Fuel LAB, a leading digital marketing and technical marketing agency based in Italy, operating worldwide. With a passion for creativity, innovation, and technology, Pietro has established himself as a thought leader in the field of digital marketing and has helped numerous companies achieve their marketing goals.