Configuring Google Analytics Enhanced Ecommerce and Google Tag Manager on Elastic Path

August 4, 2021 | Sean Power
Blog image for Configuring Google Analytics Enhanced Ecommerce and Google Tag Manager on Elastic Path

Elastic Path creates API-first, headless products that provide a front-end commerce experience. You can add Google Analytics (GA) to collect commerce data about your customers, understand how they advance through the shopping funnel, and build audiences based on the products with which users interact.

In this article, we are going to discuss how to configure GA Enhanced Ecommerce using Google Tag Manager so that you can realize the benefits of the customizability of Elastic Path products.

For quick setups, Elastic Path offers a pre-configured React PWA Reference Storefront. If you're using Google Tag Manager (GTM) elsewhere on your website, or anticipate the need to send commerce data to Google Analytics 4, then implementing via GTM instead of via the pre-configured integration is the more flexible route.

Be advised: there are three sets of documentation that already do a pretty good job of giving you step-by-step instructions to configure Ecommerce in GA as well as other general considerations (see our other articles on this topic, documentation from Google Analytics, and a guide from Simo Ahava).

This article, on the other hand, specifically focuses on considerations for Elastic Path, with only a bit of context to anchor our coverage of the topic.

If you have never set up GA Ecommerce tracking with GTM, the data layer, Universal Analytics (UA), or Google Analytics 4 (GA4), or if these concepts are new to you, then we highly, highly recommend that you come to our Google Tag Manager workshops. We give you access to a demo commerce site so that you can get hands-on practice setting up Enhanced Ecommerce on a sandbox website, and the documentation will make much more sense once you have some practice under your belt.

If you have set up Ecommerce tracking before on non-Elastic Path sites, then you are probably OK to read this blog post, make note of the Elastic Path-specific considerations, and carry on with your project.

Commerce Touchpoints and Google Analytics

With Google Analytics, you can collect data at various touchpoints in the commerce funnel:

  • Promotion impressions and clicks
  • Product impressions and clicks in lists like catalogs, category pages, site search results, and recommendation engines
  • Viewing product details/pages
  • Adding products to the cart
  • Removing products from the cart
  • Progressing through the checkout process
  • Applying product- and order-level coupons
  • Completing transactions
  • Refunds and partial refunds

Some commerce platforms automatically send commerce data to GA; others automatically push information to the data layer for you to configure in GTM; others need to be configured manually.

Elastic Path products fall into that last category: you are going to take matters into your own hands. The downside of this approach is that configuration takes a bit more planning and work to implement. The upside is that you can customize the data for your specific needs.

More customization is part of the appeal of headless commerce, so this is actually perfect if you are using Elastic Path. Let us begin.

Actually, Before We Begin: A Brief Note on Universal Analytics Enhanced Ecommerce vs. Google Analytics 4 Ecommerce

You may have heard that Google Analytics introduced a new property type called Google Analytics 4 (GA4) properties. GA4 represents an upgrade from Universal Analytics (UA) properties.

GA4 introduces a new data model. The new model has implications for your GA Ecommerce implementation.

Since GA4 is still new, Bounteous recommends parallel-tracking—that is, adding a GA4 property alongside your UA property while your organization becomes acquainted with the new data model.

You may already have one or both types of properties on your site. If you are not sure whether you have a GA4 or a UA property, you can look at your existing snippet. A UA Property ID is prefixed with UA- and looks something like UA-12345-6. A GA4 Measurement ID is prefixed with G- and looks like G-123456.

You do not need to know the details about the differences between the two property types at this point to implement commerce tracking on Elastic Path. Instead, we are going to focus on pushing the data layer Ecommerce object at key touchpoints in the user journey.

The syntax of the GA4 Ecommerce object is different from the syntax for UA. The UA syntax is compatible with both UA and GA4 property types, so that is what we are going to use to collect data in both properties in parallel.

Overview of the Process

Here is a high-level overview of what you need to do:

  1. Set up a GA4 property and a UA property if you have not already done so.
  2. Set up a GTM account and create a container if you have not already done so.
  3. Add the GTM snippets to your website.
  4. Push the eCommerce object to the data layer at key touchpoints.
  5. Add GA4 and UA to your website via GTM.
  6. Enable Enhanced Ecommerce in your UA property.
  7. QA your work and launch.

Let us dive in.

1. Set up a GA4 Property and a UA Property If You Haven't Already Done So

This part is straightforward and covered by Google's documentation, so we will not spend time on it in this blog post.

Helpful Google links:

2. Set Up a GTM Account and Create a Container if You Haven't Already Done So

This part is also straightforward and covered by Google:

Setting up and installing GTM will get you started, however, there is a best practice to highlight here.

As you use Elastic Path, you are probably going to be working in different environments such as Test, Authoring, and Live environments. This is best practice for quality assurance.

GTM offers GTM environments for the same purpose. We generally recommend creating a GTM environment for each of your development environments.

When we publish a container, we have the choice to publish to a specific GTM environment.

Each GTM environment will use its own code snippet. Your production environment will always use the "Live" environment in GTM. Its snippet will look something like this:

<!-- Google Tag Manager -->
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
<!-- End Google Tag Manager -->

Other environments will use almost exactly the same snippet. For example, suppose this is our "Test" GTM environment:

<!-- Google Tag Manager -->
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
''+i+dl+ '&gtm_auth=b50faIVZvkDzRfpKmOfF0g&gtm_preview=env-163&gtm_cookies_win=x';f.parentNode.insertBefore(j,f);
<!-- End Google Tag Manager -->

The only difference between the two snippets is the value of j.src:

Live (Production):




Notice the URL parameters added, highlighted in pink for emphasis. GTM adds three parameters to j.src in non-production environments:

  • gtm_auth
  • gtm_preview
  • gtm_cookies

We will not get into the specifics of what each of these three parameters does. Combined, they are used to distinguish between environments and determine the version of the container that loads on the page.

Whichever version of the container was published to a GTM environment will be the "live" version for that environment. This is useful for debugging, especially when using automated tools for things like regression testing. In this instance, anyone who has access to our testing environment with the "Test" GTM environment published will load the container version that has been published to "test" most recently, regardless of whether they are using the GTM preview/debug panel.

3. Add the GTM Snippets to Your Website

For each environment, you will have two snippets.

One is placed as high in the <head> of the page as possible. It looks like what we pasted above:

<!-- Google Tag Manager -->
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
<!-- End Google Tag Manager -->

The other is added immediately after the opening <body> tag. It looks like this:

<!-- Google Tag Manager (noscript) -->
<noscript><iframe src=""
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

Let's pause on the phrase, "as high in the <head> as possible."

Many technology vendors say their snippets need to be as high in the <head> as possible. To determine the true order, here are a few considerations from a GTM perspective:

Consideration One

A little further into this article, we will cover declaring a variable called dataLayer. It will be an empty array and we are going to push objects into it from time to time. Usually, these pushes happen on either page load or on user interactions. The dataLayer should be declared first and any contents available on page load should be pushed as early as possible, especially if its contents impact the user experience (UX).

For example, suppose the UX differs based on tax jurisdiction or shipping region, which you would configure in Elastic Path. We may want these variables to be available for any A/B testing or personalization that impacts the UX. Even if the UX doesn't differ by these dimensions, they may be useful from an analytics perspective. We would want this data pushed to the dataLayer as early as possible.

Consideration Two

Once the dataLayer is declared with information immediately made available, its contents can be used by A/B and personalization testing platforms like Google Optimize or Adobe Target. Your A/B testing snippet should be loaded on the page after the dataLayer is declared and after the initial dataLayer.push() is made with the pertinent properties being surfaced. When given the choice between installing your A/B testing tool via a tag manager like GTM or placing the snippet directly on the page, it is best to place the code directly on-page so that it loads synchronously to avoid a flickering UX.

Consideration Three

After the dataLayer is declared with page-level information made available and after the A/B testing/personalization tool is loaded, then load the GTM snippet.

Keep these considerations in mind as you evaluate what "as high in the <head> as possible" means for your website.

One more note: in GA, we use the word "users" to talk about website visitors. Elastic Path reserves "users" to talk about people who use the Commerce Manager dashboard to manage a store. They call website visitors "shoppers." We will try to use the word "shopper" as much as possible in this article, however "users" carries a specific meaning in GA, so it's hard to avoid in most cases. Know that there are no special considerations for "Users" in the Commerce Manager sense of the word, so forget Elastic Path's definition for now.

4. Push the eCommerce Object to the Data Layer at Key Touchpoints

The concept of the dataLayer is critical to our implementation of GA Ecommerce for Elastic Path.

What is the Data Layer?

The data layer is a JavaScript array used by Google Tag Manager and other analytics platforms to collect various types of information. Generally, the necessary information must be pushed into the data layer.

If you have not interacted with a data layer before, you might appreciate the way Himanshu Sharma describes it: the same way you can think about the DOM as the "presentation layer"—the layer of information relevant to the user—the "data layer" object contains information relevant to the business. In the context of headless commerce platforms like Elastic Path, Himanshu's analogy bears mentioning. You can also read our non-developer's guide and developer's guide to the data layer.

Most web developers are familiar with the Document Object Model (DOM). When a page loads, the DOM represents the page so that programs can change the document structure, style, and content. One way we can describe it: the DOM surfaces information about the page that is relevant to the user.

For example, a Product Details Page on a commerce website may arrange the product name, image, and description by pulling that information from a product catalog. The page presents this information to the user in the DOM.

As stated earlier, when thinking about what is meant by "data layer," it can be helpful to think about the DOM as the "presentation layer." In the same way that the DOM surfaces information used by the user, the data layer surfaces information used by the business.

image depicting how the data layer presents information that the business needs, such as store_number and stock_status

Using the Data Layer and Google Tag Manager Together

GTM listens to and interacts with the data layer to pass information from the website to analytics platforms and other tools.

Sometimes it is used to access information that is meaningful to the business but not to the user, such as store_number in the screenshot above.

Other times, it is used to streamline the process of retrieving information from the page even if it is already available in the DOM, such as image_url in the screenshot above.

If we know the name of the data layer variable that holds a certain piece of information, we can use that variable in GTM and pass its value to other platforms. We can also push events to the data layer to signal when user interactions have taken place, and then use those custom events to trigger tags in GTM. This functionality will underpin our GA eCommerce implementation.

Initializing the Data Layer

The data layer looks something like this:

<!-- Begin GTM Data Layer – example code only -->
var dataLayer = window.dataLayer = window.dataLayer || [];
  'event': 'exampleEvent',
  'id': '12345'
<!-- End GTM Data Layer -->

Note that in this example, two properties are being pushed to the data layer: event and id. We'll call your attention to event at this point. While pushing an event is not strictly necessary for all of the commerce touchpoints, we usually recommend doing so anyway. It will be useful as a custom event trigger in GTM later on.

The data layer should be declared on every page. Usually, it's placed in the file that controls the header template as early as possible so that all information is available when other tags load.

Information available on page load should be pushed to the data layer on page load. Information available after a user interaction should be pushed on user interaction.

Pushing Ecommerce Objects to the Data Layer

Remember those commerce touchpoints we discussed earlier? Those each have specific requirements for how the information needs to be made available to populate the corresponding Ecommerce reports in Google Analytics.

Google's Enhanced Ecommerce (UA) developer instructions have all of the information you need for how to structure your data layer pushes, so we will not reiterate it here. Simo Ahava's guide is also a handy reference as you add commerce tracking to your website.

Here is some guidance for each touchpoint specific to Elastic Path.

Promotion Impressions and Clicks

The ecommerce.promoView and ecommerce.promoClick objects should be pushed when a promotion appears in the viewport on your site. If these promotions appear on another site, use UTM parameters on the link click instead.

(If the promotion is on an affiliate partner and the visit results in a transaction, include the name of the affiliate in the affiliation property in ecommerce.purchase.actionField object, which is covered below.)

Google's documentation proposes making the promoView available on page load without a custom event, however, it can be helpful to push a custom event with it anyway. This is useful for situations where you have two different promos visible at the same time, such as in a ribbon at the bottom of the screen and in a modal that pops up in the center of it. Each would have its own promoView object with the same custom event, which will be used later to configure GTM.

The distinction between a promotion impression and a product impression may be unclear at first glance. It can be helpful to think of a promotion impression as a distinct offer (e.g., discount, multi-buy reward, bonus rewards points, etc.) with some kind of call to action, whereas a product impression is simply any time the product name/image is viewed without a distinct offer.

Usually, we are talking about promotions the same way Elastic Path talks about them. On the other hand, product impressions generally refer to product list impressions—that is, any time a product appears in a list alongside other products.

A common mistake we see with promotions: some teams try to use UTM parameters on internal links. This is bad. This will break the session, inflate the number of sessions you have, deflate your conversion rate, attribute any conversions to these internal UTM parameters, and erase the last-click attribution from the marketing channel that was actually responsible for bringing that user to your site. Make sure your content team knows that UTM parameters do not belong in promotions.

The other callout here is that the promoView object should be pushed only when the promotion is actually in the user's viewport. Therefore, rather than triggering this push with the pageview, we recommend pushing a custom event to the data layer and using that event to trigger the corresponding tag to be configured in GTM.

Product List Impressions and Product List Clicks

Anytime a product is viewed on your site as part of a list, the ecommerce.impressions object should be pushed to the data layer, particularly if it features a link that presents additional product details to the user.

You'll see that GA requires at least one of or Generally speaking, the ID should be the SKU of the product being viewed.

In Elastic Path, products do not have IDs. Products contain a collection of SKUs. Whereas GA expects a one-to-one relationship of product:sku, Elastic Path can feature a one-to-many relationship. In your catalog, a product may also have a one-to-many relationship with names in that product.

You'll have to make a decision here about how to handle this situation.

One option is to assign a random identifier that maps to the product collection and use that as the ID. The pro of this approach is that you meet the expectation of a one-to-one relationship, however, the con is that report consumers (e.g., a product category manager) might have no idea what your random identifier means, and that is a problem from an analytics perspective when they want to run a report on SKUs with which they are familiar.

A second option is to decide, whichever image is featured in that particular impression, is the SKU you will use. The pro here is that the SKU matches something that the report consumer is expecting to see. The con is that impressions for that particular SKU will be extremely high relative to similar SKUs in the collection and the analyst may need to make some inferences for those other SKUs for ratio metrics like Product List CTR. In this option, you will also need to decide whether to increment the product impressions count every time a user scrolls through the images of a product. If you take this approach, your impressions count may seem high, which will make the Product List CTR look deflated because impressions are so high; however, if the ID of the product impression does not match the ID of the product click, then the Product List CTR metric will be nonsensical when looking at reports with Product SKU as the primary dimension; in some cases, Product List CTR would exceed 100 percent.

Generally speaking, we would increment the product impression with each SKU that is viewed in a product list, even if that SKU is part of the same collection (or "product" in Elastic Path terms).

If you are thinking about the Product Lists available on your site, here are some places where you can configure unique list names for product list impressions/clicks:

  • Grid/category pages
  • Customer wish lists
  • Product recommendation engines (e.g., people who bought X also bought Y), distinguished by whether they appeared on a product details page, the cart, or the order confirmation page
  • Blogs featuring a list of products
  • Product search engine results pages (even if the search yielded only one result)
Viewing Product Details/Pages

Elastic Path is highly customizable, and so is Google Analytics.

UA features product-scoped custom dimensions and custom metrics. These can be included in the products array (e.g., products[].dimensionN or products[].metricN, where N represents the dimension/metric index). They help ensure that your analytics configuration is as customized as your Elastic Path setup.

Note that the UA syntax is very particular. You must name the property dimensionN and metrics must be named metricN. Keys like dN or dimN or mN or listPrice will not work. Dimensions must be strings. Metrics must be numbers. It is possible to rewrite the object via a custom javascript variable in GTM if necessary, though unless a project has a specific reason for deviating from the syntax (e.g., if GA is not the only tool using the data layer), we try not to do so.

Unlike UA, GA4 does not, at the time of this writing, have product-scoped custom definitions. They will be introduced at some point in the near future.

Also worth noting is that not all product details views happen on the product details page, so you need to choose whether you reserve this event for only product details pages or whether other ways to view product details should increment this metric.

For example, it's increasingly common to see a "Quick View" link where clicking on a product in a product list will not bring the user directly to the product details page but instead will open a tooltip with some additional details and an Add to Cart button.

For both the "Quick View" and the product details page itself, we would usually trigger the product details view, however, if we were upgrading a legacy site that sent the product details view only on the product details page, we would include additional communication around why this metric will be inflated and why metrics like Cart-to-Detail and Buy-to-Detail rates will be deflated relative to historical benchmarks.

Adding and Removing Products to and From the Cart

Conceptually, these should be straightforward, and there are no Elastic Path-specific considerations. But there are still a few things to watch for.

One of the biggest sources of confusion for both adding and removing products is getting the quantity right. The guidance here is, "don't overthink it."

If a pack of light bulbs contains six bulbs and somebody buys one pack, the value for quantity is one, not six. The quantity property is associated with the SKU/product ID or the product name (i.e., the product object in the products array). Any distinction between the 6-pack and the 24-pack of bulbs happens at the SKU level and therefore does not need to be counted six times here.

If somebody adds five packs of the same 6-pack of bulbs in a single add-to-cart action, quantity is five for that action. If they add one pack five times, quantity is one each time.

By the same logic, if a user has five packs of the same bulb in their cart and decides they only want one pack, the quantity associated with the remove-from-cart action is (5 - 1 =) 4.

Note that the remove-from cart quantity should be a positive integer. In other words, in the above scenario, quantity: 4 is correct; quantity: -4 would be incorrect.

The price property also causes confusion. The price should be the single-quantity price visible to the shopper. GA will multiply price * quantity to calculate Product Revenue in reports, so you do not need to do that calculation in your own price value. Generally, the value of instant rebates should be subtracted before the price is pushed to the data layer; mail-in rebates and checkout-level discounts should not be subtracted before price is pushed. In each of these cases, a custom metric for Discount/Rebate Amount can be useful for analysis.

Progressing Through the Checkout Process

In UA, the Checkout Behavior reports are organized according to step, and the steps can be labeled at the view level. Each step of the checkout field should include a property indicating the step (e.g., ecommerce.checkout.actionField.step, set to a number).

In GA4, we create our own funnels based on the event that carries the commerce parameters in its network request.

When you are setting up your funnel, usually you will include the same checkout steps as prompted by Elastic Path (e.g., shipment and billing info, mode of shipment, payment information, and a review step).

In GA4, you can have up to 10 steps. In UA, up to eight steps. Usually, you will not need more than four-five steps; if your custom checkout funnel is more complex than these limits, consider grouping similar steps together.

Also, the receipt/order confirmation page (i.e., the page seen after a successful transaction) should not be treated as part of the checkout and should not feature the ecommerce.checkout object. We have a separate event/action for the purchase, called purchase, which should be used instead.

Applying Product- and Order-Level Coupons

Google Analytics allows for coupons to be applied at the product level (e.g., if a particular vendor has made a discount available) and at the order level (e.g., if a store has a store-wide special discount).

For product-level coupons, include the coupon ID in the coupon property of the products array. Include it in any touchpoint from the point that it was added all the way through to the purchase.

If a product-level coupon is added during the checkout process, it should be included in the products array for the relevant products when it is added. It should not be scoped to the transaction.

If a transaction-level coupon is applied—for example, if a customer is incentivized to spend $100 and in return, they get a $10 discount on the order—then include the coupon property in the ecommerce.purchase.actionField object.

Most coupons in Elastic Path are product-level coupons, though there may be some edge cases that you need to consider. Since both Elastic Path and GA are so customizable, sometimes these fringe scenarios pop up, and we need an approach that delivers on our measurement strategy.

For example, suppose you sell donuts for $1 each, and $10 for a dozen donuts.

If you have a unique SKU for the dozen donuts, this is easy: you do not need a coupon because the SKU will have its own price point. However, you do need to decide whether to remove the 11 unique donuts from the cart on the addition of the 12th donut to avoid sending the 11 SKUs and the SKU for the dozen, but this is a product catalog decision rather than an analytics one.

However, suppose your products array contains 12 unique donuts each with its own SKU. You will need to decide whether to track those savings as an order-level coupon or whether to apply the coupon to each of the relevant SKUs in the cart. This can be tricky if the shopper has 13 donuts in the cart: a product-level coupon would only be applied to the first 12, so an order-level coupon might actually be conceptually easier to digest.

Rather than getting too bogged down in these types of edge cases, think about why you are reporting on this information: what are the business requirements? Do you need to find out which donuts are most popular in 12-packs? Are you trying to remarket strawberry turnovers to shoppers of strawberry puff donuts and need to know who are strawberry fans? Are you running a campaign for your catering service and you need to target shoppers who bought a dozen donuts to sell take-out carafes of coffee? Is the coupon part of a channel partnership and you need to report on total revenue by coupon?

Anchoring your analytics requirements to your business requirements ensures your data collection, analysis, and reporting is strategic and value-adding. If you get stuck, take a step back and ask why you want to do something and let that answer guide your decision.

Completing Transactions

For the purchase action, GA's instructions describe how to send a transaction with a pageview. We recommend using an event here, too, because we want to avoid sending duplicate transactions to GA when a shopper revisits the same page and there are some cases where we would send the pageview but not the transaction.

For example, a user may refresh the order confirmation page, or they may bookmark it, or they might follow a link in their confirmation receipt. None of these subsequent interactions should send another transaction to GA for the same purchase. All purchases should have a unique Transaction ID (the Order ID). If GA receives a duplicate transaction, it will increment all of the metrics again, inflating our reporting.

We have an article describing how to block sending duplicate transactions client-side, but with Elastic Path, it's both possible and better to manage this server-side instead.

Refunds and Partial Refunds

To issue a full refund, you need to send the Transaction ID in To issue a partial refund, send both the Transaction ID and the products/quantities being refunded.

Note that the hit carrying the refund will have a timestamp that is different from the hit carrying the purchase since you sent them at different times. This may be different from what you see in Elastic Path.

5. Add GA4 and UA to Your Website via GTM

Now that you are pushing the required values to the data layer, we can configure tags in GTM. Tags are snippets of code that, when triggered, will execute.

In the previous section, we used dot notation ( rather than bracket notation (object[property]) for references like This is deliberate: in GTM, we almost always use dot notation, even when we would otherwise use bracket notation elsewhere.

If you have followed the above guidance, then everything you set up will be an event. Simo Ahava's guide, linked earlier in this document, includes examples of how to configure the tags, so we will not reiterate it here.

Simo has also published a GA4 Ecommerce Guide, too, to walk you through dual-tagging your commerce implementation. Essentially, you will create GA4 tags that piggyback on the triggers and variables you created when you set up the tags for UA Enhanced Ecommerce.

We also like Simo's custom variable template for mapping custom dimensions and metrics to event parameters in GA4. The only thing it is missing is currency. In GA4, you need to set up currency so that your revenue populates. Add currency as a parameter to the purchase tag.

6. Enable Enhanced Ecommerce In Your UA property

Here are Google's instructions on how to do so.

7. QA Your Work and Launch

We have two resources for you as you QA your work:

Demo UA Enhanced Ecommerce and GA4 Ecommerce Reports

The Google Analytics Demo Account is a fully functional GA account that any Google user can access.

It contains a UA property configured with Enhanced Ecommerce and two GA4 properties, one of which is configured with GA4 Ecommerce. The commerce data comes from the Google Merchandise Store, a real website with live data behind it.

Access the demo account here to explore the reports in both types of GA properties. You can also see what the UA hit-level data looks like in BigQuery here.

This is what you are working toward and this is what you have to look forward to as a result of your efforts configuring GA Ecommerce tracking!

Final Thoughts

Configuring GA Ecommerce is no small feat, but it is well worth the effort, particularly when it is customized to an analytics strategy designed and tailored for your Elastic Path store.

While all of the analytics touchpoints add value, if you are like most development teams with which we have worked, you are strapped for resources and need to prioritize and organize your implementation according to sprints. If that sounds like you, read about our recommended order of implementation to ensure the highest-value touchpoints are configured first as you plan out your development cycles.