A well-designed dataLayer is the difference between a maintainable analytics setup and a tag-management nightmare. Here's how we design ours.
Naming conventions
Use snake_case for event names and parameters. Match GA4's recommended event names where they exist (purchase, add_to_cart, sign_up). Stay consistent across all platforms — don't have add_to_cart in GA4 and AddToCart in Meta.
Event structure
Every event push should have at minimum:
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: 'T-29183',
value: 49.99,
currency: 'USD',
items: [...]
}
});
Common pitfalls
- Mixed types — value sometimes string, sometimes number. Pick one (number) and stick.
- Missing ecommerce.clear() — events bleed into each other on SPAs without explicit clearing.
- Page-load timing — pushing events before GTM loads. Use the gtm.dom or gtm.load triggers.
Server-side dataLayer (server-side GTM)
For server-side GTM, you're pushing events to your sGTM container endpoint. The shape is similar but transmitted via fetch/POST instead of window.dataLayer.