- GA4
- Data Layer
- Ecommerce Tracking
- Google Tag Manager
GA4 Ecommerce Data Layer: The Correct Structure
By Olam Sule · Published 3 Sept 2026
TL;DR
A GA4 ecommerce data layer is the structured JavaScript object your site pushes to window.dataLayer at each shopping step: view_item, add_to_cart, begin_checkout and purchase, each carrying an items array. Get the object right and GTM forwards clean data to GA4. Get the data types, the items array or the transaction_id wrong and the revenue in your reports stops matching real orders. We build and fix these for clients most weeks.
Olamide Sule, founder of Dolphin Analytics: a digital analytics expert based in London delivering solutions for agency and in-house clients.
We build and repair GA4 ecommerce data layers for brands and their agencies most weeks, and the pattern rarely changes. The GTM tags look fine, the events fire, and the revenue in GA4 still disagrees with the orders in the finance export. Almost always the fault is in the data layer, not the tags reading it. So this is the spec we actually hand a developer: the object structure for each ecommerce event, what every parameter means, and the mistakes that make a clean setup report wrong numbers.
What is a GA4 ecommerce data layer?
A GA4 ecommerce data layer is the structured JavaScript object your site pushes
to window.dataLayer for each shopping step, carrying a GA4 event name and an
ecommerce object with an items array. Google Tag Manager
reads that object and forwards it to GA4. The data layer is where the numbers
are decided; GTM shapes and moves them but never invents them.
That single fact tells you where to look when something breaks. If a purchase value is wrong, the data layer sent it wrong, and every tag downstream passed the mistake along. What Universal Analytics called “enhanced ecommerce” is now just this event schema in GA4, so the concept carries over even though the event names and structure changed.
Which events belong in the GA4 ecommerce data layer?
GA4 has a defined set of ecommerce events, and each maps to one step a shopper
takes. You do not need every event on day one, but the checkout and purchase
events are not optional if you want revenue in GA4. Every event carries the same
items array, so the shape you build once gets reused all the way down the
funnel.
| Shopping step | GA4 event | Event-level fields it needs |
|---|---|---|
| Sees a product list | view_item_list | item_list_id, item_list_name |
| Clicks a product in a list | select_item | the clicked item |
| Views a product | view_item | value, currency |
| Adds to basket | add_to_cart | value, currency |
| Views the basket | view_cart | value, currency |
| Starts checkout | begin_checkout | value, currency |
| Enters payment details | add_payment_info | payment_type |
| Completes the order | purchase | transaction_id, value, currency |
The items array rides on every one of those events.
Google’s GA4 ecommerce events reference
lists the full set and each required parameter, and it is the source worth
checking before you build.
How do you structure a GA4 ecommerce data layer push?
Push each ecommerce event to window.dataLayer as an object with two parts: the
event name GTM triggers on, and an ecommerce object holding the parameters.
Clear the previous ecommerce object first so nothing leaks between events. Here
is a view_item push, the pattern every other event follows:
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null }); // clear the previous object
window.dataLayer.push({
event: "view_item",
ecommerce: {
currency: "GBP",
value: 64.25,
items: [
{
item_id: "SKU-123",
item_name: "Example product",
item_brand: "Example brand",
item_category: "Accessories",
price: 64.25,
quantity: 1
}
]
}
});
add_to_cart and begin_checkout are the same object with a different event
name and, for the checkout events, the full basket in items. The purchase
event is the one that has to be right, because it carries the revenue:
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "T-10492",
value: 128.5,
currency: "GBP",
tax: 4.28,
shipping: 3.99,
items: [
{ item_id: "SKU-123", item_name: "Example product", price: 64.25, quantity: 2 }
]
}
});
The transaction_id must be unique per order. GA4 uses it to discard a
duplicate purchase if a shopper reloads the confirmation page or returns to it
from an email, so a stable, unique ID is what stops refreshed pages inflating
your revenue. On Shopify, WooCommerce or
a headless build, a plugin, app or the platform’s own events produce these
pushes; on a custom build, your developer writes them against this shape. Once
the data layer is right, wiring the GTM tags that read it is a separate job we
walk through in ecommerce tracking in Google Tag Manager.
What does each item parameter mean?
The items array is the spine of the whole schema, so getting its parameters
right once pays off on every event. Each object in the array describes one
product. Only an identifier is strictly required, but a thin items array
produces thin reports.
| Parameter | Required? | What it holds |
|---|---|---|
item_id | One of id or name | Your SKU or product ID |
item_name | One of id or name | The human-readable product name |
price | Recommended | Unit price, as a number |
quantity | Recommended | Units, as a number |
item_brand | Optional | The product brand |
item_category | Optional | Category; item_category2 to item_category5 add depth |
item_variant | Optional | Variant, such as size or colour |
item_list_id / item_list_name | Optional | Which list the product appeared in |
index | Optional | Position of the product in that list |
coupon | Optional | An item-level coupon code |
discount | Optional | Item-level discount, as a number |
Send item_id and item_name on every product so listings, product-detail and
purchase reports all join up on the same identifier. If the ID drifts between
view_item and purchase, GA4 treats them as two different products and the
funnel falls apart.
Where do GA4 ecommerce data layers go wrong?
This is the part most tutorials skip, and it is why a data layer that looks finished still sends numbers you cannot trust. When we audit an ecommerce setup, the same structural faults come up again and again, and none of them are in the GTM tags.
- Prices and values sent as strings.
price,quantity,valueanddiscounthave to be numeric. A value quoted as"128.5"instead of128.5can be dropped when GA4 calculates revenue, so the purchase shows the right items and no money. Multi-currency and templated themes trip on this most. - An empty items array. The
purchasefires, the value is right, butitemsis blank because the data layer never populated it at that step. Revenue looks fine; product-performance reports are quietly useless. - No
ecommerce: nullclear between events. Skip the clearing push and theitemsfrom aview_itemcan linger and attach to a lateradd_to_cartorpurchase, so GA4 receives products that were never part of that action. - A missing or reused
transaction_id. With no unique order ID, a reloaded confirmation page firespurchaseagain and GA4 counts two sales for one order. This is the most common reason GA4 revenue reads higher than the finance export. - The push lands after the tag reads it. GTM fires on the event, but if the
ecommerceobject is assembled a moment later by another script, the tag reads an object that is not there yet. Push the complete object in one go, before anything that depends on it.
When we audit accounts, the empty or mistyped items array is the fault we find
most, and it hides well: the revenue total looks healthy, so nobody suspects the
data layer until someone opens a product report and finds it blank. A data layer
also only reports what it is told to push, which is its own blind spot. On one
subscription box brand we audited, subscriptions were 32% of UK revenue and 71%
of US revenue, and invisible to every marketing tool, because recurring renewals
never fired a browser purchase event at all.
How do you check your GA4 ecommerce data layer is correct?
Validate the data layer in three places, in order, because each one proves something the others cannot.
- Read the raw pushes in the console. Open the browser dev tools, and in
the Console tab type
window.dataLayerand press enter. Expand the array and confirm each ecommerce action pushed an object with the righteventname and a populatedecommerceobject. This is the data as your site actually wrote it, before GTM touched it. - Inspect the object in GTM Preview. In GTM click Preview, enter your
URL, and walk a test purchase. In the preview rail, select the
purchaseevent and open the Data Layer tab: theecommerceobject should be complete, withtransaction_id,value,currencyand a fullitemsarray. - Confirm delivery in GA4 DebugView. Open GA4 Admin > DebugView, pick
your debug device, and expand the
purchaseevent. Check it fired once, theitemsarray arrived populated, andvalueandcurrencyare present. DebugView shows the event as GA4 received it, which Preview mode alone cannot.
The third check is the one people skip, and it is the one that catches silent failures like a value that survived the data layer but got dropped in transit. For the wider tracking picture, our GA4 events explained guide covers how GA4’s event model fits together, and consent settings can block these events before they ever reach GA4, which reads like a data layer fault but is not one.
What we do when the data layer is the problem
When a client’s ecommerce numbers do not match their orders, we do not start by rebuilding tags. We watch a real purchase in Preview mode and DebugView, then trace each event back to the data layer that fed it. The fix is nearly always one of the faults above, in a specific place, not a full rebuild, and we do this work for agency and in-house teams most weeks, so the diagnosis stays quick even on a large container.
If you want to see what is firing on your site right now, before deciding anything, Sonar scans a URL from the outside in one click and reports the tags and pixels it can prove from that scan, with no account needed. When you already know the ecommerce events are wrong and want them fixed properly, our paid tracking audit goes inside the GA4 and GTM account and returns a written findings report naming each broken event, its impact and the fix.
FAQ
What is a GA4 ecommerce data layer?
A GA4 ecommerce data layer is a JavaScript object your site pushes to
window.dataLayer for each shopping step, holding a GA4 event name (like
view_item or purchase) and an ecommerce object with an items array.
Google Tag Manager reads that object and forwards it to GA4. The data layer is
where the numbers are decided; GTM only passes on what it finds there, which is
why a weak data layer is the root of most ecommerce tracking problems.
What does a GA4 purchase data layer push look like?
A purchase push clears the previous ecommerce object, then pushes an object
with event set to "purchase" and an ecommerce object holding
transaction_id, value, currency and a populated items array. The
transaction_id must be unique per order so GA4 can drop a duplicate if the
confirmation page reloads. value and currency sit at the event level; the
products sit inside items. Google’s GA4 ecommerce reference lists every
required parameter.
Why set ecommerce to null before each data layer push?
Pushing ecommerce: null before each ecommerce event clears the previous object
so old values do not leak into the next event. Without it, an items array from
a view_item event can linger and attach itself to a later add_to_cart or
purchase, so GA4 receives products that were never part of that action. It is
one line, and skipping it is a common cause of items appearing on the wrong
event.
Should item prices be numbers or strings in the data layer?
Numbers. price, quantity, value and discount must be sent as numeric
types, not quoted strings. A price sent as "64.25" instead of 64.25 can be
dropped or mishandled when GA4 calculates revenue, so a purchase can show the
right items but no value. Multi-currency and templated stores get this wrong
most often, because the price is stitched into a string somewhere in the theme.
How do I check my GA4 ecommerce data layer is correct?
Check it in three places. Type window.dataLayer in the browser console to read
the raw pushes, open the Data Layer tab in GTM Preview to see what each event
carried, then confirm in GA4 DebugView that the event arrived once with a
populated items array and a value. DebugView shows the event as GA4 actually
received it, which the other two cannot prove on their own.
Frequently asked
What is a GA4 ecommerce data layer?
A GA4 ecommerce data layer is a JavaScript object your site pushes to window.dataLayer for each shopping step, holding a GA4 event name (like view_item or purchase) and an ecommerce object with an items array. Google Tag Manager reads that object and forwards it to GA4. The data layer is where the numbers are decided; GTM only passes on what it finds there, which is why a weak data layer is the root of most ecommerce tracking problems.
What does a GA4 purchase data layer push look like?
A purchase push clears the previous ecommerce object, then pushes an object with event set to "purchase" and an ecommerce object holding transaction_id, value, currency and a populated items array. The transaction_id must be unique per order so GA4 can drop a duplicate if the confirmation page reloads. value and currency sit at the event level; the products sit inside items. Google's GA4 ecommerce reference lists every required parameter.
Why set ecommerce to null before each data layer push?
Pushing ecommerce: null before each ecommerce event clears the previous object so old values do not leak into the next event. Without it, an items array from a view_item event can linger and attach itself to a later add_to_cart or purchase, so GA4 receives products that were never part of that action. It is one line, and skipping it is a common cause of items appearing on the wrong event.
Should item prices be numbers or strings in the data layer?
Numbers. price, quantity, value and discount must be sent as numeric types, not quoted strings. A price sent as "64.25" instead of 64.25 can be dropped or mishandled when GA4 calculates revenue, so a purchase can show the right items but no value. Multi-currency and templated stores get this wrong most often, because the price is stitched into a string somewhere in the theme.
How do I check my GA4 ecommerce data layer is correct?
Check it in three places. Type window.dataLayer in the browser console to read the raw pushes, open the Data Layer tab in GTM Preview to see what each event carried, then confirm in GA4 DebugView that the event arrived once with a populated items array and a value. DebugView shows the event as GA4 actually received it, which the other two cannot prove on their own.