Data Layer Design
How to architect a robust, maintainable data layer for analytics.
What Is a Data Layer?
A data layer is a JavaScript object that stores structured information about a page and user interactions. It acts as a standardized interface between your website and marketing/analytics tools.
window.dataLayer = window.dataLayer || [];
Core Principles
1. Standardization
Use consistent naming and structure across all pages:
// Good - consistent structure
dataLayer.push({
event: 'page_view',
page: {
type: 'product',
category: 'electronics',
name: 'Wireless Headphones'
}
});
// Bad - inconsistent structure
dataLayer.push({
pageType: 'product', // different naming
category: 'Electronics', // different casing
productName: 'Wireless Headphones' // nested differently
});
2. Event-Driven
Push events for significant user actions:
// User adds item to cart
dataLayer.push({
event: 'add_to_cart',
ecommerce: {
items: [{
item_id: 'SKU_001',
item_name: 'Wireless Headphones',
price: 99.99,
quantity: 1
}]
}
});
3. Separation of Concerns
The data layer should contain:
- ✅ What happened (event type)
- ✅ Business context (product info, user type)
- ✅ Structured values (prices, quantities)
The data layer should NOT contain:
- ❌ Platform-specific formatting
- ❌ Pixel IDs or tracking codes
- ❌ PII (email, phone, names)
Schema Design
Page Data
{
page: {
type: string, // 'home', 'product', 'category', 'checkout'
category: string, // Primary category
subcategory: string, // Secondary category (optional)
name: string, // Page title or identifier
locale: string, // 'en-US', 'fr-FR'
environment: string // 'production', 'staging'
}
}
User Data
{
user: {
id: string, // Hashed user ID (never PII)
type: string, // 'guest', 'registered', 'premium'
authenticated: boolean,
customer_tier: string // 'bronze', 'silver', 'gold'
}
}
E-commerce Data
Follow GA4's recommended e-commerce schema:
{
ecommerce: {
currency: 'USD',
value: 99.99,
items: [{
item_id: 'SKU_001',
item_name: 'Product Name',
affiliation: 'Store Name',
coupon: 'SUMMER_SALE',
discount: 10.00,
index: 0,
item_brand: 'Brand',
item_category: 'Category',
item_category2: 'Subcategory',
item_list_id: 'related_products',
item_list_name: 'Related Products',
item_variant: 'Blue / Large',
price: 99.99,
quantity: 1
}]
}
}
Implementation Patterns
Initial Page Load
Push page context before any events:
// At page load (before GTM container)
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
page: {
type: 'product',
category: 'electronics'
},
user: {
authenticated: true,
type: 'premium'
}
});
Event Sequence
For multi-step processes, track each step:
// Checkout flow
dataLayer.push({ event: 'begin_checkout', ecommerce: {...} });
dataLayer.push({ event: 'add_shipping_info', ecommerce: {...} });
dataLayer.push({ event: 'add_payment_info', ecommerce: {...} });
dataLayer.push({ event: 'purchase', ecommerce: {...} });
SPA Handling
For single-page applications, clear and repush on navigation:
// Clear previous e-commerce data
dataLayer.push({ ecommerce: null });
// Push new page data
dataLayer.push({
event: 'virtual_page_view',
page: {
type: 'category',
category: 'new_arrivals'
}
});
Documentation
Data Layer Specification
Create a detailed spec document including:
| Event | Description | Parameters | Example |
|-------|-------------|------------|---------|
| page_view | Page loaded | page.type, page.category | {event: 'page_view', page: {type: 'home'}} |
| add_to_cart | Item added to cart | ecommerce.items | See e-commerce schema |
| form_submit | Form submitted | form.name, form.type | {event: 'form_submit', form: {name: 'contact'}} |
Developer Integration Guide
Provide developers with:
- Implementation examples for each event type
- Validation rules for required fields
- Testing procedures for QA
- Troubleshooting guide for common issues
Validation
Automated Testing
Implement data layer tests:
// Example Jest test
test('add_to_cart event has required fields', () => {
const event = window.dataLayer.find(e => e.event === 'add_to_cart');
expect(event).toBeDefined();
expect(event.ecommerce.items).toHaveLength(1);
expect(event.ecommerce.items[0].item_id).toBeDefined();
});
Console Monitoring
Add debugging helper:
// Development environment only
window.dataLayer.push = function(...args) {
console.log('dataLayer.push:', ...args);
return Array.prototype.push.apply(this, args);
};
Common Mistakes
Avoid These
-
Direct assignment instead of push
// Wrong dataLayer.page = { type: 'home' }; // Correct dataLayer.push({ page: { type: 'home' } }); -
Not clearing e-commerce object
// Wrong - previous data may persist dataLayer.push({ event: 'view_item', ecommerce: {...} }); // Correct - clear first dataLayer.push({ ecommerce: null }); dataLayer.push({ event: 'view_item', ecommerce: {...} }); -
Including PII
// Wrong dataLayer.push({ user: { email: '[email protected]' } }); // Correct dataLayer.push({ user: { id: 'hashed_user_id' } });
Previous: Conversion Tracking