As single page apps become more prevalent, you will likely encounter an eCommerce implementation tracked by Google Tag Manager at some point. If you’re using Google Analytics on an eComm site, you should also be leveraging Google’s Enhanced eCommerce feature. For those who have worked with GTM’s data layer, implementation should be relatively straightforward. If it isn’t I recommend checking out these articles. Just a quick caveat – I didn’t read any of those articles, though I did a CTRL-F for eCommerce instructions. My point is that there is plenty of information out there showing you how to build a stock implementation. I couldn’t find a single one addressing Enhanced eCommerce in GTM for single page apps! So here’s my attempt to guide you through that implementation.
I’m making a few assumptions here. The first assumption is that you know how to implement basic Enhanced eCommerce via GTM’s data layer. The second assumption is that you know how to implement basic tracking for SPAs. If you don’t know how to implement tracking for SPAs, here are a few articles. That’s from Page 1 in Google. I didn’t read a single one, so I hope they aren’t crappy or misleading.
Table of Contents
The Problem
For single page apps, you can’t just drop in the eCommerce data layer like you do with inferior, normal-loading pages. That would be too easy and Lunametrics would have already written 25 articles on it. As you know, Enhanced eCommerce relies on the data layer to function. We also know to take special consideration for data layers on SPAs. While values might change, so much is dependent upon sequencing and inheritance. I’ll explain.
What does sequencing mean?
Data must be set and sent in a specific order for accurate attribution. For instance, I don’t want to send category page impression tags on a PDP. Similarly, I want to be able to package all of my product data with a pageview. While you CAN send Enhanced eCommerce data via an event, I wouldn’t recommend it unless it’s absolutely necessary. We also want to avoid this with SPA implementations. That means setting data layer values in a specific order. Consider the following situation and solution:
A user is on the category page, clicks on a product, and is taken to a product detail page (PDP)
- Set the impression data layer
- Fire pageview for category page (Enhanced eCommerce data is passed to GTM)
- User clicks on a product
- Set the product click data layer
- Fire the product click event (Enhanced eCommerce data is passed to GTM)
- Set the eCommerce Data Layer
- Fire the pageview (Enhanced eCommerce data is passed to GTM)
Whew, that’s a little exhausting. Everything has to happen in that specific order for it to work. If sequencing is off, we run into problems with inheritance.
What does inheritance mean?
Inheritance means things are passed downstream. In Google Tag Manager, it might mean that a value is inherited from a cookie, data layer, or other source. For instance, at some point in my lifetime I’ve logged into a site and the site saved a custom dimension showing I’m a registered user. Since that data is stored as a custom dimension on the User scope, I will inherit that value the next time I arrive to the site whether I’m logged in or not because that was the last value that was passed. The eCommerce data layer works in a similar way. It processes the most recent eCommerce value passed into the data layer. Using the same sequence from before:
- User clicks on a product
- Set the product click data layer
- Fire the product click event (Enhanced eCommerce data is passed to GTM)
- Set the eCommerce Data Layer
- Fire the pageview (Enhanced eCommerce data is passed to GTM)
- User navigates to the homepage
- Fire a pageview (Enhanced eCommerce data is passed to GTM)
- User navigates to the blog
- Fire a pageview (Enhanced eCommerce data is passed to GTM)
eComm data is being inherited on the homepage and the blog pageviews due to the old eCommerce objects that stuck around in the data layer. How? Due to inheritance and sequencing. Let’s tie these two together.
The Solution
Let’s knock out the easy part first – sequencing. The best way to nip sequencing in the bud is by passing an event with your product data and using that to trigger your pageview (or event, but that’s obvious). We need to create 3 triggers:
The first 2 on this list will trigger tags on any page (or history change event) EXCEPT for eCommerce pages. We’re assuming product/category/checkout/etc all have /products/ in the URL. The last trigger is for when you need to package product data with a tag… it’s very important. For instance, if we want to fire a tag for a PDP and package Enhanced eCommerce with the pageview, the data layer would look something like this:
dataLayer.push({
'event': 'ecommerce'
'ecommerce': {
'detail': {
'actionField': {'list': 'Apparel Gallery'}, // 'detail' actions have an optional list property.
'products': [{
'name': 'Triblend Android T-Shirt', // Name or ID is required.
'id': '12345',
'price': '15.25',
'brand': 'Google',
'category': 'Apparel',
'variant': 'Gray'
}]
}
}
});
Your event trigger should target an event named ‘ecommerce’. This event will fire the pageview. Below is what the tag would look like once you have applied all of the filters:
So everywhere else you’ll use a standard pageview; HOWEVER, for pages where you want to fire off Enhanced eCommerce you must trigger the pageview from the event. That will work. There’s one more thing we have to do. Notice the Tag firing priority is set. It’s there to solve for the inheritance problem. Remember each and every time you fire a pageview, GTM will scan the entire data layer starting with the last object that was pushed and work its way towards the first until it finds an eCommerce object. When navigating from a PDP to the homepage, GTM will grab the last eComm object passed to the data layer regardless of the page you’re on.
We have 2 options to resolve this:
- Have 2 Google Analytics settings variables – one with Enhanced eCommerce enabled and one without. This means you’ll have to create 2 separate tags, one for eCommerce pages and one for non-eCommerce pages. I’ve tried this and I don’t really recommend it because it requires more maintenance.
- Pass a dummy eCommerce object to fake out GTM.
Number 1 is pretty straightforward, conceptually… so let’s take a look at option 2. I’ll talk about why I prefer this one.
Passing a dummy eCommerce object
We want to solve for inheritance. Google Tag Manager will be searching from the last data layer object to the first until it finds an eCommerce object… any eCommerce object (valid or not). With each eCommerce tag, I also fire a tag that pushes a blank eCommerce object into the data layer. To do this, create the following event:
This trigger is initiated on every Enhanced eCommerce action (your names may differ). The trigger is then paired with the following tag:
Here’s a version you can copy/paste:
<script>dataLayer.push({ 'ecommerce': {}}); </script>
Feel free to try this code out in your console, as well. Pay attention to the Tag firing priority attribute for each of these tags:
When users navigate from page-to-page, Google Tag Manager will clear out the eCommerce variable. As long as the pageview tag has a higher number than the dataLayer JavaScript, you’re good! Here’s the sequence:
- Product page loads (via history change or otherwise)
- eComm event fires, picking up the Enhanced eComm product data
- dataLayer.push() tag fires immediately after, “clearing” the object
That’s actually it. With anything that’s tag management, there always seems to be multiple ways to do one thing. This is the way I do it. I hope this helps.
This is fantastic.
Thanks Neil!
Hi Jim,
so overall: instead of using history change triggers, pageviews are hooked onto eCom-Events which makes total sense to me.
One basic understanding I still miss:
If the SPA is running within a traditional round-trip page, the GTM Snippet is most likely included in the head tag outside the app. Will the dataLayer payload/ecom-snippet, which is placed with a push inside the app, reach the GTM snippet outside the app?
This is a very basic question, but sth I don’t really get.
Thanks a lot,
Chris
Hey Chris – Depends on the context. If the dataLayer is defined anywhere on the page, it will reach GTM in some way, shape, or form. You can update the dataLayer object through the dataLayer.push() parameters like normal and choose whether you want to reset it on each virtual page load or if you want to just append more data to the object.
Hi,
Thank you for the article!
Why do you suggest not to send Enhanced Ecommerce data via custom events (“unless it’s absolutely necessary”)?
What is the advantage of enabling E-Ecommerce on the main Universal Analytics pageview tag, instead of exclusively using custom events (as triggers) and dedicated tags for sending E-Ecommerce data to Google Analytics?
Hi
I have a problem with single page application and GA4.
I implemented eccommerce tracking with datalayar and developer.
But the number of eevents are lower than reality.