Shopify Data Layer: What It Is and How to Build One for GA4 Ecommerce
By Alessandro Colarossi, Founder, Triangle Digital · Published · 9 min read
Short answer
A Shopify data layer is a JavaScript array, window.dataLayer, that your store fills with structured details about what is happening (the product viewed, the cart, the order) so Google Tag Manager can pass the same clean data to GA4, Google Ads, Meta and other tags. Shopify does not ship one for GTM, so you build it: Liquid snippets in your theme for storefront pages and a Customer Events pixel for checkout, both pushing GA4-format ecommerce events like view_item, add_to_cart and purchase.
What is a data layer?
A data layer is a plain JavaScript array that sits between your website and your tags. Instead of every marketing tag scraping the page for a product name or price, your site pushes that information into the array once, in a predictable shape, and Google Tag Manager reads it.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'view_item',
ecommerce: {
currency: 'USD',
value: 49.0,
items: [{ item_id: 'TEE-BLK-M', item_name: 'Everyday Tee', price: 49.0, quantity: 1 }]
}
});The event key is what GTM listens for. Each push with an event value can fire a Custom Event trigger, and everything else in the object becomes available to Data Layer Variables.
Why a Shopify data layer matters
- One source of truth. GA4, Google Ads, Meta, TikTok and Pinterest all read the same values, so revenue and item numbers match across platforms.
- Tags stop breaking when the theme changes. Tags that scrape CSS selectors fail when a theme update renames a class. A data layer does not care how the page looks.
- It unlocks GA4 ecommerce reports. Product, cart and checkout funnels need the
itemsarray that GA4 expects. - Easier debugging. You can inspect
window.dataLayerin the browser console or in GTM Preview and see exactly what was sent.
How Shopify changes the picture
Shopify gives you two places to produce data layer events, and a complete setup uses both:
| Source | Runs on | Good for |
|---|---|---|
| Theme Liquid snippet | Storefront pages (home, collection, product, cart) | Page-level context: view_item_list, view_item, view_cart. Liquid knows the product, collection and cart objects. |
| Customer Events custom pixel | Storefront and checkout, sandboxed | Interactions and checkout: add_to_cart, remove_from_cart, begin_checkout, add_shipping_info, add_payment_info, purchase. |
Because the theme and the pixel can both see storefront events, decide who owns each event so nothing is reported twice. The pixel side is covered in the Customer Events guide.
Use the GA4 ecommerce format
Push events in the structure GA4 documents for ecommerce. That way GA4 tags need almost no mapping, and platforms like Google Ads can reuse the same objects. These are the events a full Shopify store should send:
| GA4 event | Fires when | Typical source on Shopify |
|---|---|---|
view_item_list | A collection or search results list is shown | Theme snippet (collection page) |
view_item | A product page is viewed | Theme snippet (product page) |
add_to_cart | A product is added to the cart | Customer Events (product_added_to_cart) |
remove_from_cart | A product is removed from the cart | Customer Events (product_removed_from_cart) |
view_cart | The cart is viewed | Customer Events (cart_viewed) or theme snippet |
begin_checkout | Checkout starts | Customer Events (checkout_started) |
add_shipping_info | Shipping step is submitted | Customer Events (checkout_shipping_info_submitted) |
add_payment_info | Payment step is submitted | Customer Events (payment_info_submitted) |
purchase | Order is completed | Customer Events (checkout_completed) |
search | A site search is made | Customer Events (search_submitted) |
Example: a view_item snippet in Liquid
Create a snippet (Online Store → Themes → Edit code → Snippets → Add a new snippet) named data-layer, then include it with {% render 'data-layer' %} near the top of <head> in theme.liquid, above the GTM snippet. This example covers the product page. It is illustrative: the tagging packs ship a complete, tested version covering every storefront event.
{%- if request.page_type == 'product' -%}
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null }); // clear the previous ecommerce object
window.dataLayer.push({
event: 'view_item',
ecommerce: {
currency: {{ cart.currency.iso_code | json }},
value: {{ product.selected_or_first_available_variant.price | divided_by: 100.0 }},
items: [{
item_id: {{ product.selected_or_first_available_variant.sku | default: product.id | json }},
item_name: {{ product.title | json }},
item_brand: {{ product.vendor | json }},
item_category: {{ product.type | json }},
item_variant: {{ product.selected_or_first_available_variant.title | json }},
price: {{ product.selected_or_first_available_variant.price | divided_by: 100.0 }},
quantity: 1
}]
}
});
</script>
{%- endif -%}Two details matter here. The | json filter safely escapes quotes in product titles, and ecommerce: null is pushed first so values from a previous event do not leak into the next one (Google recommends this when pushing ecommerce objects).
{%- if request.page_type == 'collection' -%}
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'view_item_list',
ecommerce: {
item_list_id: {{ collection.handle | json }},
item_list_name: {{ collection.title | json }},
items: [
{%- for product in collection.products limit: 24 -%}
{
item_id: {{ product.selected_or_first_available_variant.sku | default: product.id | json }},
item_name: {{ product.title | json }},
price: {{ product.price | divided_by: 100.0 }},
index: {{ forloop.index }}
}{% unless forloop.last %},{% endunless %}
{%- endfor -%}
]
}
});
</script>
{%- endif -%}Reading the data layer in Google Tag Manager
- Create a Custom Event trigger whose event name matches the push, for example
view_item. - Create a GA4 Event tag with the same event name, tick Send Ecommerce data, and choose Data Layer as the source. GA4 then reads
ecommerce.items,valueandcurrencyautomatically. - For other tags (Google Ads, Meta, TikTok), create Data Layer Variables such as
ecommerce.valueandecommerce.items, and use them in the tag fields. - Open Preview, trigger the event on your store, and confirm the data layer message and the tag firing in Tag Assistant.
Rules that prevent most data layer bugs
- Send numbers as numbers.
value: 49.0, notvalue: "$49.00". Strings with currency symbols break GA4 and Google Ads values. - Always include `currency` (ISO code like USD) on events that carry
value. - Keep `item_id` consistent across every event and, for Google Ads remarketing, aligned with the IDs in your Merchant Center feed. Feeds created by Shopify's Google channel typically use a
shopify_<country>_<productId>_<variantId>pattern, so check yours before choosing an ID scheme. - Clear the ecommerce object with
{ ecommerce: null }before each ecommerce push. - Define `window.dataLayer` before GTM loads so early pushes are not lost.
- Use a unique `transaction_id` (the Shopify order ID) on
purchase, so platforms can de-duplicate.
Want the full data layer without writing it?
The Shopify DIY Tagging Packs include the complete data layer snippet and Customer Events pixel, already mapped to the GA4 ecommerce event set and wired to a ready-to-import Google Tag Manager container. Pair this guide with the GTM setup walkthrough and the GA4 ecommerce guide.
Frequently asked questions
Does Shopify have a built-in data layer for Google Tag Manager?
No. Shopify exposes data through Liquid objects on the storefront and through Customer Events in checkout, but it does not create a GTM data layer for you. You build one with a theme snippet and a custom pixel, or use a pre-built pack.
What is dataLayer.push in Shopify?
It is the JavaScript call that adds an event or data object to window.dataLayer. Google Tag Manager watches the array and can fire tags when it sees a push containing an event key.
Where does the Shopify data layer code go?
Storefront data layer code goes in a theme snippet rendered from theme.liquid, above the GTM snippet. Checkout events are pushed from a custom pixel under Settings → Customer events.
Do I need a data layer if I use the Google & YouTube app?
The app does its own tracking and does not give you a data layer you control. If you want Google Tag Manager to manage tags, you need your own data layer. See the comparison.
Why is my GA4 ecommerce report empty?
Common causes are a missing items array, value sent as a string, no currency, or the purchase event only being installed in the theme and never reaching checkout. Check with the debugging guide.
