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_idecommerce.valueecommerce.currencyecommerce.taxecommerce.shippingecommerce.itemsecommerce.couponproduct_idcustomer_typepayment_methodshipping_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
- Click Preview in GTM
- Navigate to your WooCommerce site
- Check Data Layer tab in GTM debugger
- Verify all expected values appear
Common Issues
Data Layer undefined:
- Initialize
dataLayerbefore 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
- Initialize early: Create
dataLayerbefore GTM loads - Use consistent naming: Follow GA4 eCommerce spec
- Deduplicate purchases: Check tracking flag before pushing
- Handle AJAX: Use WooCommerce AJAX events for cart updates
- Test thoroughly: Verify all events in GTM Preview mode
- Monitor performance: Limit data layer size, avoid large arrays
Next Steps
- GTM Setup - Install GTM on WooCommerce
- GA4 Event Tracking - Configure GA4 tags
- Troubleshoot Tracking - Debug data layer issues