Skip to main content

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:

  1. Implementation examples for each event type
  2. Validation rules for required fields
  3. Testing procedures for QA
  4. 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

  1. Direct assignment instead of push

    // Wrong
    dataLayer.page = { type: 'home' };
    
    // Correct
    dataLayer.push({ page: { type: 'home' } });
    
  2. 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: {...} });
    
  3. Including PII

    // Wrong
    dataLayer.push({ user: { email: '[email protected]' } });
    
    // Correct
    dataLayer.push({ user: { id: 'hashed_user_id' } });
    

Previous: Conversion Tracking