Google Analytics 4 Data Layer Setup | OpsBlu Docs

Google Analytics 4 Data Layer Setup

How to configure data layer variables and user properties for Google Analytics 4. Covers data preparation, custom properties, event enrichment, and.

Overview

The data layer serves as a structured intermediary between your website and Google Tag Manager, ensuring GA4 receives consistent, reliable data. A well-implemented data layer simplifies tracking configuration, reduces maintenance overhead, and enables sophisticated analytics without fragile DOM scraping.

Data Layer Architecture

Initialization

Initialize the data layer before any GTM or analytics code loads:

<script>
  window.dataLayer = window.dataLayer || [];
  dataLayer.push({
    'pageType': 'product',
    'pageCategory': 'electronics',
    'userId': 'user_12345',
    'userStatus': 'logged_in',
    'consentState': {
      'analytics_storage': 'granted',
      'ad_storage': 'denied'
    }
  });
</script>
<!-- GTM container code follows -->

Event-Based Pushes

Trigger events with associated data:

dataLayer.push({
  'event': 'add_to_cart',
  'ecommerce': {
    'currency': 'USD',
    'value': 79.99,
    'items': [{
      'item_id': 'SKU-12345',
      'item_name': 'Wireless Headphones',
      'item_category': 'Electronics',
      'item_category2': 'Audio',
      'price': 79.99,
      'quantity': 1
    }]
  }
});

Required Fields by Page Type

All Pages

Field Type Description Example
pageType string Page classification home, category, product, cart, checkout, confirmation
pageCategory string Content grouping electronics, blog, support
userId string Authenticated user ID user_12345 (hash or stable ID)
userStatus string Login state guest, logged_in, subscriber
consentState object Current consent settings See consent section

Product Detail Pages

dataLayer.push({
  'event': 'view_item',
  'ecommerce': {
    'currency': 'USD',
    'value': 79.99,
    'items': [{
      'item_id': 'SKU-12345',
      'item_name': 'Wireless Headphones',
      'item_brand': 'AudioTech',
      'item_category': 'Electronics',
      'item_category2': 'Audio',
      'item_category3': 'Headphones',
      'item_variant': 'Black',
      'price': 79.99,
      'quantity': 1
    }]
  }
});

Category/Listing Pages

dataLayer.push({
  'event': 'view_item_list',
  'ecommerce': {
    'item_list_id': 'electronics_audio',
    'item_list_name': 'Audio Equipment',
    'items': [
      {
        'item_id': 'SKU-12345',
        'item_name': 'Wireless Headphones',
        'index': 0,
        'price': 79.99
      },
      {
        'item_id': 'SKU-67890',
        'item_name': 'Bluetooth Speaker',
        'index': 1,
        'price': 49.99
      }
    ]
  }
});

Cart Page

dataLayer.push({
  'event': 'view_cart',
  'ecommerce': {
    'currency': 'USD',
    'value': 142.97,
    'items': [
      {
        'item_id': 'SKU-12345',
        'item_name': 'Wireless Headphones',
        'price': 79.99,
        'quantity': 1
      },
      {
        'item_id': 'SKU-67890',
        'item_name': 'USB-C Cable',
        'price': 12.99,
        'quantity': 2
      }
    ]
  }
});

Checkout Flow

// Begin checkout
dataLayer.push({
  'event': 'begin_checkout',
  'ecommerce': {
    'currency': 'USD',
    'value': 142.97,
    'coupon': 'SUMMER10',
    'items': [/* cart items */]
  }
});

// Add shipping info
dataLayer.push({
  'event': 'add_shipping_info',
  'ecommerce': {
    'currency': 'USD',
    'value': 142.97,
    'shipping_tier': 'Ground',
    'items': [/* cart items */]
  }
});

// Add payment info
dataLayer.push({
  'event': 'add_payment_info',
  'ecommerce': {
    'currency': 'USD',
    'value': 142.97,
    'payment_type': 'Credit Card',
    'items': [/* cart items */]
  }
});

Order Confirmation

dataLayer.push({
  'event': 'purchase',
  'ecommerce': {
    'transaction_id': 'ORD-98765',
    'value': 156.96,
    'tax': 8.00,
    'shipping': 5.99,
    'currency': 'USD',
    'coupon': 'SUMMER10',
    'items': [
      {
        'item_id': 'SKU-12345',
        'item_name': 'Wireless Headphones',
        'affiliation': 'Online Store',
        'coupon': 'SUMMER10',
        'discount': 8.00,
        'price': 79.99,
        'quantity': 1
      }
    ]
  }
});

GA4 Ecommerce Item Schema

Every item in the items array should follow this structure:

Parameter Type Required Description
item_id string Yes Product SKU or ID
item_name string Yes Product name
affiliation string No Store or vendor
coupon string No Product-level coupon
discount number No Discount amount
index number No Position in list
item_brand string No Brand name
item_category string No Primary category
item_category2-5 string No Sub-categories
item_list_id string No List identifier
item_list_name string No List display name
item_variant string No Color, size, etc.
location_id string No Physical location
price number No Unit price
quantity number No Quantity (default: 1)

Push consent defaults before GTM loads:

window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// Set defaults before consent is gathered
gtag('consent', 'default', {
  'analytics_storage': 'denied',
  'ad_storage': 'denied',
  'ad_user_data': 'denied',
  'ad_personalization': 'denied',
  'wait_for_update': 500
});

When user grants consent:

gtag('consent', 'update', {
  'analytics_storage': 'granted',
  'ad_storage': 'granted'
});

dataLayer.push({
  'event': 'consent_update',
  'consentState': {
    'analytics_storage': 'granted',
    'ad_storage': 'granted'
  }
});

Conditional Data Layer Content

Modify data layer based on consent:

const userData = {};

if (consentState.analytics_storage === 'granted') {
  userData.userId = getCurrentUserId();
  userData.userProperties = getUserProperties();
}

if (consentState.ad_storage === 'granted') {
  userData.gclid = getGclid();
  userData.advertisingId = getAdvertisingId();
}

dataLayer.push(userData);

Campaign Data

UTM Parameters

Capture and persist campaign data:

const urlParams = new URLSearchParams(window.location.search);

dataLayer.push({
  'campaign': {
    'source': urlParams.get('utm_source'),
    'medium': urlParams.get('utm_medium'),
    'name': urlParams.get('utm_campaign'),
    'content': urlParams.get('utm_content'),
    'term': urlParams.get('utm_term'),
    'gclid': urlParams.get('gclid'),
    'dclid': urlParams.get('dclid')
  }
});

Click ID Persistence

Store click IDs for attribution:

// On landing, store gclid in first-party cookie
if (urlParams.get('gclid')) {
  document.cookie = `_gclid=${urlParams.get('gclid')}; max-age=7776000; path=/`;
}

// Include in data layer for server-side events
dataLayer.push({
  'gclid': getCookie('_gclid')
});

GTM Variable Configuration

Data Layer Variables

Create GTM variables to read data layer values:

Variable Name Variable Type Data Layer Variable Name
DLV - pageType Data Layer Variable pageType
DLV - userId Data Layer Variable userId
DLV - ecommerce.value Data Layer Variable ecommerce.value
DLV - ecommerce.items Data Layer Variable ecommerce.items

Constant Variables

Store configuration values:

Variable Name Value
Const - GA4 Measurement ID G-XXXXXXXXXX
Const - GA4 API Secret (for server-side)

Schema Versioning and Governance

Version Tracking

Include schema version for debugging:

dataLayer.push({
  'schemaVersion': '2.1.0',
  'pageType': 'product',
  // ... rest of data
});

Documentation Requirements

Maintain a schema document that includes:

  • All data layer variables and their types
  • Required vs. optional fields per page type
  • Enumerated values (e.g., valid pageType values)
  • Ownership and change approval process
  • Deprecation timeline for removed fields

Change Management

  1. Propose changes via pull request or change ticket
  2. Review impact on GTM configuration and GA4 reports
  3. Test in staging environment with GTM Preview
  4. Deploy with version bump and stakeholder notification
  5. Monitor for data quality issues post-deployment

Validation and Debugging

Browser Console

// View full data layer
console.table(dataLayer);

// Monitor pushes in real-time
(function() {
  const originalPush = dataLayer.push;
  dataLayer.push = function() {
    console.log('dataLayer.push:', arguments[0]);
    return originalPush.apply(this, arguments);
  };
})();

GTM Preview Mode

  1. Enable Preview in GTM
  2. Navigate through key pages and actions
  3. Check the Variables tab for each event
  4. Verify values match expected data layer content

GA4 DebugView

  1. Install the GA Debugger Chrome extension
  2. Navigate your site
  3. View events and parameters in DebugView
  4. Confirm ecommerce items render correctly

Common Validation Checks

Check Expected Result
ecommerce.items populated Array with required item fields
transaction_id unique No duplicates in purchase events
Currency code valid ISO 4217 format (USD, EUR, GBP)
Price values numeric No strings or formatted values
userId present when logged in Stable, non-PII identifier