← All Guides

GA4 Ecommerce dataLayer Specification: Every Event, Every Item Parameter, With Copy-Paste Code for Developers

A complete dataLayer specification for GA4 ecommerce that also serves Google Ads and Meta: the event list, the exact structure of the ecommerce object and items array, required and optional parameters, the reset rule, list and promotion events, refunds, and a validation checklist.

Atiqul Islam
Written by Atiqul Islam

Tracking & analytics specialist. 278+ GTM, GA4 and server-side tracking projects for clients in the US, UK and Australia.

Rather not do this yourself? I set this up for clients every week. Free 15-minute audit, no obligation.

Book a Free Audit

This is the ecommerce dataLayer specification I hand to developers. It follows GA4's recommended events exactly, so GA4's reports work without configuration, and it carries everything Google Ads and Meta tags need. Implement it once and every platform reads from the same source.

Global Rules

The Item Object

Parameter Type Required Notes
item_id string Yes SKU or variant ID
item_name string Yes Product name
price number Yes Unit price after item-level discount
quantity number Yes Integer
item_brand string Recommended
item_category string Recommended Broadest category
item_category2 to item_category5 string Optional Narrower levels
item_variant string Recommended Size, colour
discount number Optional Unit discount applied
coupon string Optional Item-level coupon
index number Optional Position in a list
item_list_id, item_list_name string Optional Where it was selected from
affiliation string Optional Store or seller

Event Specification

view_item_list

Fires when a list of products renders, once per list. Include up to the first 20 items.

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'view_item_list',
  ecommerce: {
    item_list_id: 'category_cushions',
    item_list_name: 'Cushions',
    items: [{ item_id: 'SKU-401', item_name: 'Lumbar Support Cushion', price: 59.95, quantity: 1, index: 0, item_list_id: 'category_cushions', item_list_name: 'Cushions' }]
  }
});

select_item

Fires when a product is clicked in a list.

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'select_item',
  ecommerce: {
    item_list_id: 'category_cushions',
    item_list_name: 'Cushions',
    items: [{ item_id: 'SKU-401', item_name: 'Lumbar Support Cushion', price: 59.95, quantity: 1, index: 0 }]
  }
});

view_item

Fires on the product page, once per load, with the selected or default variant.

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'view_item',
  ecommerce: {
    currency: 'AUD',
    value: 59.95,
    items: [{ item_id: 'SKU-401', item_name: 'Lumbar Support Cushion', item_brand: 'Posture Co', item_category: 'Cushions', item_variant: 'Grey', price: 59.95, quantity: 1 }]
  }
});

add_to_cart and remove_from_cart

Fire on confirmed cart API success. value is price times quantity for the items changed, not the cart total.

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'add_to_cart',
  ecommerce: {
    currency: 'AUD',
    value: 119.90,
    items: [{ item_id: 'SKU-401', item_name: 'Lumbar Support Cushion', item_variant: 'Grey', price: 59.95, quantity: 2 }]
  }
});

view_cart

Fires on the cart page with all items and the cart value.

begin_checkout

Fires when checkout starts, with all items, value and coupon if applied.

add_shipping_info and add_payment_info

Fire when the respective step completes, with all items, value, and shipping_tier or payment_type.

purchase

Fires on the order confirmation page, once per order, guarded by the order ID.

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: '10234',
    value: 129.90,
    tax: 11.81,
    shipping: 9.95,
    currency: 'AUD',
    coupon: 'WELCOME10',
    items: [{ item_id: 'SKU-401', item_name: 'Lumbar Support Cushion', item_brand: 'Posture Co', item_category: 'Cushions', item_variant: 'Grey', price: 59.95, quantity: 2 }]
  },
  user_email: 'name@example.com',
  user_phone: '+61400000000',
  customer_type: 'new'
});

value is the order total excluding tax and shipping unless the business decides otherwise, and the decision must be applied everywhere. user_email and user_phone feed Enhanced Conversions and Advanced Matching; omit them if the business prefers.

refund

Fires when a refund is processed, usually server-side or from an admin action; for full refunds send transaction_id and value; for partial refunds include the refunded items.

view_promotion and select_promotion

Fire when a banner is visible or clicked, with promotion_id, promotion_name, creative_name, creative_slot and the items it promotes if applicable.

Deduplication Guard for purchase

(function () {
  var orderId = '10234';
  try {
    if (localStorage.getItem('purchase_' + orderId)) { return; }
    localStorage.setItem('purchase_' + orderId, '1');
  } catch (e) {}
  // purchase push here
})();

On server-rendered platforms, also mark the order as tracked server-side so the push is not output on reload.

How Tags Read It

Validation Checklist for the Developer

Outcome

One specification, one implementation, and GA4, Google Ads and Meta all reporting the same products, values and orders. When a number disagrees between platforms, you check attribution settings, not the data.

Frequently Asked Questions

Why does the ecommerce object need to be reset?

GTM merges dataLayer pushes into one model. If the previous push had an ecommerce object with items, the next push without a full ecommerce object inherits the old items. Pushing { ecommerce: null } first clears it.

What is the minimum for a purchase event to work?

transaction_id, value, currency and an items array with item_id, item_name, price and quantity on each item. Without transaction_id, refreshes double count. Without items, product reports are empty.

Do I need both item_id and item_name?

Send both. GA4 keys product reports on item_id and displays item_name. Use the SKU or variant ID for item_id so the same product maps to the same row across sessions.

How many item_category levels can I send?

Five: item_category through item_category5. Send them from the broadest to the most specific, consistently.

Can the same dataLayer serve Meta and Google Ads?

Yes. Meta reads value, currency and the items mapped to content_ids. Google Ads reads value, currency and transaction_id. One push, three platforms.

Want This Done Right the First Time?

Book a free 15-minute audit call. I'll review your setup, show you what is broken, and tell you exactly what it takes to fix it.

Chat Now