The GTM Data Layer Guide
The dataLayer is the contract between your site and your tracking. Schema design, ecommerce schema, custom events, identity passing, and the patterns that hold across replatforms.
What the dataLayer is
The dataLayer is a JavaScript array that lives on every page where GTM is installed. Your code pushes events and structured data to it. GTM reads from it. The dataLayer is the contract between your website and your tracking — defined once, referenced by every tag.
The decision to take the dataLayer seriously is the difference between a tracking stack that survives replatforms and a tracking stack that breaks every time the marketing team changes pages.
The basic push
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ event: 'add_to_cart', ecommerce: { currency: 'USD', value: 49.99, items: [{ item_id: 'SKU-123', item_name: 'Linen Throw', price: 49.99, quantity: 1, item_category: 'Home', item_variant: 'Charcoal' }] }
});
The pattern that survives
- Initialize the dataLayer before the GTM snippet. Put
window.dataLayer = window.dataLayer || [];in the head before GTM loads. - Define your schema in a separate document. List every event your site fires, with all its parameters. Treat it like an API specification.
- Use the GA4 ecommerce schema for ecommerce events. Even if you do not use GA4. The schema is standard and integrations expect it.
- Push events at the right moment. Page load, button click, form submit. Use server-rendered pushes for purchase confirmations to avoid losing data on fast back-button.
- Reset the ecommerce object before each push.
window.dataLayer.push({ ecommerce: null });then push the new event. Prevents stale data from previous events from leaking into the new one.
Standard event types
For ecommerce, use GA4's recommended event names: view_item_list, select_item, view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase, refund.
For lead gen: generate_lead, sign_up, login, search.
For content engagement: scroll_depth, video_start, video_progress, video_complete, file_download, outbound_click.
For custom: any event name in snake_case. Keep them descriptive and consistent.
Identity passing
Pass authenticated user identifiers via dataLayer so GTM can route them downstream:
window.dataLayer.push({ event: 'user_data', user: { id: 'CUST-987654', email_hashed: 'a1b2c3...', logged_in: true, customer_segment: 'vip' }
});
GTM can then set the GA4 user_id parameter, pass external_id to Meta CAPI, and forward to Klaviyo, HubSpot, or your CDP.
Common dataLayer failure modes
Push before initialization
Some libraries push to the dataLayer before window.dataLayer = [] runs. The push silently disappears. Initialize first.
Stale ecommerce data
Two events fired in sequence both reference the same ecommerce object. The second event's items array includes the first event's items. Reset with { ecommerce: null } between pushes.
Pushing on the wrong moment
Purchase event pushed only via inline JavaScript on the thank-you page. User uses fast back-button before the script runs. Conversion lost. Server-render the push or fire on DOMContentLoaded.
Inconsistent item_id across events
view_item uses SKU, add_to_cart uses variant ID, purchase uses internal product ID. GA4 cannot join the funnel. Use the same item_id everywhere.
What to read next
Sister pages: advanced GTM setup, GTM server-side container, iframe tracking with GTM. For ecommerce schema specifics, read advanced GA4 ecommerce tracking.