WooCommerce Data Layer for Google Tag Manager | OpsBlu Docs

WooCommerce Data Layer for Google Tag Manager

Complete reference for WooCommerce eCommerce data layer structure, variables, and implementation with GTM

The data layer is the bridge between WooCommerce and Google Tag Manager, providing structured product, cart, and order data for tracking. This guide covers the complete WooCommerce data layer implementation.

What is the Data Layer?

The data layer is a JavaScript object that holds information about the page, user, and eCommerce data:

window.dataLayer = window.dataLayer || [];
dataLayer.push({
    'event': 'purchase',
    'ecommerce': {
        'transaction_id': '12345',
        'value': 199.99,
        'items': [...]
    }
});

GTM reads this data to:

  • Trigger tags based on events
  • Pass eCommerce data to analytics platforms
  • Create variables for conditional logic

Complete WooCommerce Data Layer Structure

Product Page (view_item)

dataLayer.push({
    'event': 'view_item',
    'ecommerce': {
        'currency': 'USD',
        'value': 99.99,
        'items': [{
            'item_id': 'SKU123',                    // Product SKU or ID
            'item_name': 'Premium T-Shirt',          // Product name
            'item_brand': 'Brand Name',              // Product brand
            'item_category': 'Apparel',              // Primary category
            'item_category2': 'Mens',                // Secondary category
            'item_category3': 'Shirts',              // Tertiary category
            'item_variant': 'Blue, Large',           // Product variation
            'price': 99.99,                          // Unit price
            'quantity': 1                            // Quantity (always 1 for views)
        }]
    },
    // Additional WooCommerce data
    'product_id': 123,
    'product_type': 'simple',                         // simple, variable, grouped, etc.
    'stock_status': 'instock',
    'sku': 'SKU123'
});

Implementation:

add_action('woocommerce_after_single_product', 'wc_product_datalayer');
function wc_product_datalayer() {
    global $product;
    if (!$product) return;

    $categories = get_the_terms($product->get_id(), 'product_cat');
    $category_names = array();
    if ($categories && !is_wp_error($categories)) {
        foreach ($categories as $cat) {
            $category_names[] = $cat->name;
        }
    }

    $data = array(
        'event' => 'view_item',
        'ecommerce' => array(
            'currency' => get_woocommerce_currency(),
            'value' => (float) $product->get_price(),
            'items' => array(
                array(
                    'item_id' => $product->get_sku() ? $product->get_sku() : $product->get_id(),
                    'item_name' => $product->get_name(),
                    'item_brand' => get_product_brand($product),
                    'item_category' => isset($category_names[0]) ? $category_names[0] : '',
                    'item_category2' => isset($category_names[1]) ? $category_names[1] : '',
                    'item_variant' => $product->is_type('variation') ? $product->get_attribute_summary() : '',
                    'price' => (float) $product->get_price(),
                    'quantity' => 1
                )
            )
        ),
        'product_id' => $product->get_id(),
        'product_type' => $product->get_type(),
        'stock_status' => $product->get_stock_status(),
        'sku' => $product->get_sku()
    );
    ?>
    <script>
        dataLayer.push(<?php echo json_encode($data); ?>);
    </script>
    <?php
}

function get_product_brand($product) {
    $brands = get_the_terms($product->get_id(), 'product_brand');
    return ($brands && !is_wp_error($brands)) ? $brands[0]->name : '';
}

Product List (view_item_list)

dataLayer.push({
    'event': 'view_item_list',
    'ecommerce': {
        'item_list_id': 'category_apparel',
        'item_list_name': 'Category: Apparel',
        'items': [
            {
                'item_id': 'SKU123',
                'item_name': 'Premium T-Shirt',
                'item_category': 'Apparel',
                'price': 99.99,
                'index': 1                           // Position in list
            },
            {
                'item_id': 'SKU456',
                'item_name': 'Classic Jeans',
                'item_category': 'Apparel',
                'price': 149.99,
                'index': 2
            }
        ]
    }
});

Implementation:

add_action('woocommerce_after_shop_loop', 'wc_product_list_datalayer');
function wc_product_list_datalayer() {
    global $wp_query;

    if (!$wp_query->have_posts()) return;

    $items = array();
    $position = 1;

    foreach ($wp_query->posts as $post) {
        $product = wc_get_product($post->ID);
        if (!$product) continue;

        $items[] = array(
            'item_id' => $product->get_sku() ? $product->get_sku() : $product->get_id(),
            'item_name' => $product->get_name(),
            'item_category' => get_product_category($product),
            'price' => (float) $product->get_price(),
            'index' => $position
        );
        $position++;
    }

    $list_name = get_list_name();
    ?>
    <script>
        dataLayer.push({
            'event': 'view_item_list',
            'ecommerce': {
                'item_list_id': '<?php echo sanitize_title($list_name); ?>',
                'item_list_name': '<?php echo esc_js($list_name); ?>',
                'items': <?php echo json_encode($items); ?>
            }
        });
    </script>
    <?php
}

function get_list_name() {
    if (is_product_category()) {
        return 'Category: ' . single_cat_title('', false);
    } elseif (is_shop()) {
        return 'Shop Page';
    }
    return 'Product List';
}

function get_product_category($product) {
    $cats = get_the_terms($product->get_id(), 'product_cat');
    return ($cats && !is_wp_error($cats)) ? $cats[0]->name : '';
}

Add to Cart (add_to_cart)

dataLayer.push({
    'event': 'add_to_cart',
    'ecommerce': {
        'currency': 'USD',
        'value': 99.99,                              // Price × quantity
        'items': [{
            'item_id': 'SKU123',
            'item_name': 'Premium T-Shirt',
            'item_category': 'Apparel',
            'item_variant': 'Blue, Large',
            'price': 99.99,
            'quantity': 1
        }]
    }
});

Implementation (AJAX-Compatible):

add_action('wp_footer', 'wc_addtocart_datalayer');
function wc_addtocart_datalayer() {
    ?>
    <script>
        // Track AJAX add to cart (shop pages)
        jQuery(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
            var product = $button.closest('.product');
            var productId = $button.data('product_id');
            var quantity = $button.data('quantity') || 1;
            var productName = product.find('.woocommerce-loop-product__title').text() || $button.attr('aria-label');
            var priceText = product.find('.price .amount').first().text();
            var price = parseFloat(priceText.replace(/[^0-9.-]+/g, ''));

            dataLayer.push({
                'event': 'add_to_cart',
                'ecommerce': {
                    'currency': '<?php echo get_woocommerce_currency(); ?>',
                    'value': price * quantity,
                    'items': [{
                        'item_id': productId,
                        'item_name': productName,
                        'price': price,
                        'quantity': quantity
                    }]
                }
            });
        });

        // Track non-AJAX add to cart (product pages)
        jQuery('form.cart').on('submit', function() {
            var quantity = parseInt(jQuery('input.qty').val()) || 1;

            // Product data will be available from PHP
            <?php if (is_product()) : global $product; ?>
            dataLayer.push({
                'event': 'add_to_cart',
                'ecommerce': {
                    'currency': '<?php echo get_woocommerce_currency(); ?>',
                    'value': <?php echo (float) $product->get_price(); ?> * quantity,
                    'items': [{
                        'item_id': '<?php echo $product->get_sku() ? $product->get_sku() : $product->get_id(); ?>',
                        'item_name': '<?php echo esc_js($product->get_name()); ?>',
                        'price': <?php echo (float) $product->get_price(); ?>,
                        'quantity': quantity
                    }]
                }
            });
            <?php endif; ?>
        });
    </script>
    <?php
}

View Cart (view_cart)

dataLayer.push({
    'event': 'view_cart',
    'ecommerce': {
        'currency': 'USD',
        'value': 249.98,                             // Cart total
        'items': [
            {
                'item_id': 'SKU123',
                'item_name': 'Premium T-Shirt',
                'price': 99.99,
                'quantity': 2
            },
            {
                'item_id': 'SKU456',
                'item_name': 'Classic Jeans',
                'price': 149.99,
                'quantity': 1
            }
        ]
    }
});

Implementation:

add_action('woocommerce_after_cart', 'wc_cart_datalayer');
function wc_cart_datalayer() {
    $cart = WC()->cart;
    if ($cart->is_empty()) return;

    $items = array();
    foreach ($cart->get_cart() as $cart_item) {
        $product = $cart_item['data'];
        $items[] = array(
            'item_id' => $product->get_sku() ? $product->get_sku() : $product->get_id(),
            'item_name' => $product->get_name(),
            'item_category' => get_product_category($product),
            'price' => (float) $product->get_price(),
            'quantity' => $cart_item['quantity']
        );
    }
    ?>
    <script>
        dataLayer.push({
            'event': 'view_cart',
            'ecommerce': {
                'currency': '<?php echo get_woocommerce_currency(); ?>',
                'value': <?php echo $cart->get_cart_contents_total(); ?>,
                'items': <?php echo json_encode($items); ?>
            }
        });
    </script>
    <?php
}

Begin Checkout (begin_checkout)

dataLayer.push({
    'event': 'begin_checkout',
    'ecommerce': {
        'currency': 'USD',
        'value': 259.98,                             // Cart total including shipping/tax
        'coupon': 'SAVE10',                          // Applied coupon(s)
        'items': [...]
    }
});

Implementation:

add_action('woocommerce_before_checkout_form', 'wc_checkout_datalayer');
function wc_checkout_datalayer() {
    static $tracked = false;
    if ($tracked) return;
    $tracked = true;

    $cart = WC()->cart;
    $items = array();

    foreach ($cart->get_cart() as $cart_item) {
        $product = $cart_item['data'];
        $items[] = array(
            'item_id' => $product->get_sku() ? $product->get_sku() : $product->get_id(),
            'item_name' => $product->get_name(),
            'price' => (float) $product->get_price(),
            'quantity' => $cart_item['quantity']
        );
    }
    ?>
    <script>
        dataLayer.push({
            'event': 'begin_checkout',
            'ecommerce': {
                'currency': '<?php echo get_woocommerce_currency(); ?>',
                'value': <?php echo $cart->get_total('raw'); ?>,
                'coupon': '<?php echo implode(', ', $cart->get_applied_coupons()); ?>',
                'items': <?php echo json_encode($items); ?>
            }
        });
    </script>
    <?php
}

Purchase (purchase)

dataLayer.push({
    'event': 'purchase',
    'ecommerce': {
        'transaction_id': '12345',                   // Unique order ID
        'affiliation': 'Online Store',               // Store name
        'value': 259.98,                             // Order total
        'tax': 19.99,                                // Tax amount
        'shipping': 10.00,                           // Shipping cost
        'currency': 'USD',
        'coupon': 'SAVE10',                          // Coupon used
        'items': [
            {
                'item_id': 'SKU123',
                'item_name': 'Premium T-Shirt',
                'item_category': 'Apparel',
                'item_variant': 'Blue, Large',
                'price': 99.99,
                'quantity': 2
            },
            {
                'item_id': 'SKU456',
                'item_name': 'Classic Jeans',
                'item_category': 'Apparel',
                'price': 149.99,
                'quantity': 1
            }
        ]
    },
    // Additional order data
    'order_id': 12345,
    'payment_method': 'Credit Card',
    'shipping_method': 'Standard Shipping',
    'customer_type': 'returning'                     // new or returning
});

Implementation:

add_action('woocommerce_thankyou', 'wc_purchase_datalayer', 10, 1);
function wc_purchase_datalayer($order_id) {
    if (!$order_id) return;

    // Prevent duplicate tracking
    if (get_post_meta($order_id, '_datalayer_purchase_tracked', true)) {
        return;
    }

    $order = wc_get_order($order_id);
    $items = array();

    foreach ($order->get_items() as $item) {
        $product = $item->get_product();
        $items[] = array(
            'item_id' => $product->get_sku() ? $product->get_sku() : $product->get_id(),
            'item_name' => $item->get_name(),
            'item_category' => get_product_category($product),
            'item_variant' => $product->is_type('variation') ? $product->get_attribute_summary() : '',
            'price' => (float) ($item->get_total() / $item->get_quantity()),
            'quantity' => $item->get_quantity()
        );
    }

    // Determine customer type
    $customer_email = $order->get_billing_email();
    $customer_orders = wc_get_orders(array(
        'billing_email' => $customer_email,
        'status' => array('wc-completed', 'wc-processing'),
        'limit' => -1
    ));
    $customer_type = count($customer_orders) <= 1 ? 'new' : 'returning';

    $data = array(
        'event' => 'purchase',
        'ecommerce' => array(
            'transaction_id' => $order->get_order_number(),
            'affiliation' => get_bloginfo('name'),
            'value' => (float) $order->get_total(),
            'tax' => (float) $order->get_total_tax(),
            'shipping' => (float) $order->get_shipping_total(),
            'currency' => $order->get_currency(),
            'coupon' => implode(', ', $order->get_coupon_codes()),
            'items' => $items
        ),
        'order_id' => $order->get_id(),
        'payment_method' => $order->get_payment_method_title(),
        'shipping_method' => $order->get_shipping_method(),
        'customer_type' => $customer_type
    );
    ?>
    <script>
        dataLayer.push(<?php echo json_encode($data); ?>);
    </script>
    <?php

    update_post_meta($order_id, '_datalayer_purchase_tracked', true);
}

Variable Products Data Layer

Track Variation Selection

// When customer selects a variation
dataLayer.push({
    'event': 'view_item_variant',
    'ecommerce': {
        'currency': 'USD',
        'value': 99.99,
        'items': [{
            'item_id': 'SKU123-BLUE-L',              // Variation SKU
            'item_name': 'Premium T-Shirt',
            'item_variant': 'Blue, Large',           // Selected attributes
            'price': 99.99
        }]
    },
    'variation_id': 456,
    'attributes': {
        'color': 'Blue',
        'size': 'Large'
    }
});

Implementation:

add_action('woocommerce_after_add_to_cart_button', 'wc_variation_datalayer');
function wc_variation_datalayer() {
    global $product;
    if (!$product->is_type('variable')) return;
    ?>
    <script>
        jQuery('.variations_form').on('found_variation', function(event, variation) {
            dataLayer.push({
                'event': 'view_item_variant',
                'ecommerce': {
                    'currency': '<?php echo get_woocommerce_currency(); ?>',
                    'value': variation.display_price,
                    'items': [{
                        'item_id': variation.sku || variation.variation_id,
                        'item_name': '<?php echo esc_js($product->get_name()); ?>',
                        'item_variant': Object.values(variation.attributes).join(', '),
                        'price': variation.display_price
                    }]
                },
                'variation_id': variation.variation_id,
                'attributes': variation.attributes
            });
        });
    </script>
    <?php
}

Using Data Layer Variables in GTM

Create Data Layer Variables

For each eCommerce parameter, create a Data Layer Variable in GTM:

Transaction ID:

Variable Type: Data Layer Variable
Data Layer Variable Name: ecommerce.transaction_id
Variable Name: DLV - Transaction ID

Create variables for:

  • ecommerce.transaction_id
  • ecommerce.value
  • ecommerce.currency
  • ecommerce.tax
  • ecommerce.shipping
  • ecommerce.items
  • ecommerce.coupon
  • product_id
  • customer_type
  • payment_method
  • shipping_method

Use Variables in Tags

In GA4 Event Tags, reference these variables:

Event Parameters:
- transaction_id: {{DLV - Transaction ID}}
- value: {{DLV - Value}}
- currency: {{DLV - Currency}}
- items: {{DLV - Items}}

Custom WooCommerce Data Layer

Add Customer Lifetime Value

add_filter('woocommerce_gtm_datalayer', 'add_customer_ltv', 10, 1);
function add_customer_ltv($data) {
    if (is_user_logged_in()) {
        $customer = new WC_Customer(get_current_user_id());
        $data['customer_ltv'] = $customer->get_total_spent();
        $data['customer_order_count'] = $customer->get_order_count();
    }
    return $data;
}

Add Subscription Data

// For WooCommerce Subscriptions
add_action('woocommerce_thankyou', 'subscription_datalayer', 20, 1);
function subscription_datalayer($order_id) {
    if (function_exists('wcs_order_contains_subscription')) {
        if (wcs_order_contains_subscription($order_id)) {
            $subscriptions = wcs_get_subscriptions_for_order($order_id);
            foreach ($subscriptions as $subscription) {
                ?>
                <script>
                    dataLayer.push({
                        'event': 'subscription_purchase',
                        'subscription_id': '<?php echo $subscription->get_id(); ?>',
                        'billing_period': '<?php echo $subscription->get_billing_period(); ?>',
                        'billing_interval': <?php echo $subscription->get_billing_interval(); ?>,
                        'subscription_total': <?php echo $subscription->get_total(); ?>
                    });
                </script>
                <?php
            }
        }
    }
}

Debugging the Data Layer

View in Browser Console

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

// View eCommerce events only
dataLayer.filter(item => item.ecommerce).forEach(console.log);

// View specific event
dataLayer.filter(item => item.event === 'purchase');

// Check latest push
console.log(dataLayer[dataLayer.length - 1]);

Use GTM Preview Mode

  1. Click Preview in GTM
  2. Navigate to your WooCommerce site
  3. Check Data Layer tab in GTM debugger
  4. Verify all expected values appear

Common Issues

Data Layer undefined:

  • Initialize dataLayer before GTM container loads

Values missing:

  • Check product/order objects exist
  • Verify WooCommerce functions are available

Events not firing:

  • Ensure event name matches GTM trigger exactly
  • Check for JavaScript errors in console

Best Practices

  1. Initialize early: Create dataLayer before GTM loads
  2. Use consistent naming: Follow GA4 eCommerce spec
  3. Deduplicate purchases: Check tracking flag before pushing
  4. Handle AJAX: Use WooCommerce AJAX events for cart updates
  5. Test thoroughly: Verify all events in GTM Preview mode
  6. Monitor performance: Limit data layer size, avoid large arrays

Next Steps