GTM recipe: a clean e-commerce data layer specification
A copy-paste data layer spec for e-commerce sites — the events, the parameters and the GTM variable setup that keeps GA4 and Meta consistent.
Most tracking problems are data layer problems wearing a disguise. This recipe is the specification we hand to client dev teams — steal it.
The events
Implement exactly these, named exactly this way (they map 1:1 to GA4's recommended e-commerce events):
view_item— product page viewadd_to_cartbegin_checkoutadd_payment_infopurchase
The purchase push
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null }); // clear the previous object
window.dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "T-10345", // MUST be unique per order
value: 128.40, // order total ex-shipping, number not string
currency: "GBP",
items: [{
item_id: "SKU-2231",
item_name: "Oak Side Table",
price: 128.40,
quantity: 1,
item_category: "Furniture"
}]
}
});
Three rules that prevent 90% of the bugs we get called in to fix:
- Clear before you push. The
{ ecommerce: null }reset stops old items leaking into new events. - Numbers are numbers.
"128.40"as a string breaks revenue in subtle, quiet, expensive ways. transaction_idis sacred. It's how GA4 deduplicates and how server-side events match browser events. Never omit it, never reuse it.
GTM variable setup
Create Data Layer Variables (version 2) for each field: ecommerce.transaction_id, ecommerce.value, ecommerce.currency, ecommerce.items. Then every tag — GA4, Google Ads, Meta — reads from the same variables. One source, no per-tag drift.
QA checklist
- Purchase fires once per order (test a page refresh on the thank-you page — the classic double-count).
- Values match the order management system to the penny for 20 sample orders.
- Events fire with consent granted and are correctly suppressed with consent denied.
itemsarray is populated on every event, not just purchase.
Run that checklist quarterly. Data layers rot the moment a developer touches the checkout.