WordPress Data Layer for GTM
Learn how to structure and populate the GTM data layer with WordPress-specific data for advanced tracking and personalization.
Understanding the Data Layer
The data layer is a JavaScript object that passes information from WordPress to Google Tag Manager (and other tools). It acts as a bridge between your CMS and your tags.
Benefits of a Proper Data Layer
- Decouples tracking from presentation - change templates without breaking tags
- Enables advanced personalization - target users based on WP data
- Simplifies tag management - tags pull from consistent variables
- Improves debugging - centralized data source for troubleshooting
Data Layer Structure
dataLayer = [{
// Page metadata
'pageType': 'blog-post',
'pageName': 'How to Use WordPress',
'pageCategory': 'Tutorials',
// User information
'userStatus': 'logged-in',
'userRole': 'subscriber',
// Content attributes
'postID': '123',
'postAuthor': 'John Doe',
'publishDate': '2024-01-15'
}];
Basic WordPress Data Layer
Initialize Data Layer Before GTM
Always initialize the data layer before GTM loads:
add_action('wp_head', 'initialize_data_layer', 0); // Priority 0 runs first
function initialize_data_layer() {
?>
<script>
window.dataLayer = window.dataLayer || [];
</script>
<?php
}
add_action('wp_head', 'populate_wordpress_data_layer', 1);
function populate_wordpress_data_layer() {
// Gather WordPress data
$page_type = 'unknown';
if (is_front_page()) {
$page_type = 'homepage';
} elseif (is_single()) {
$page_type = 'blog-post';
} elseif (is_page()) {
$page_type = 'page';
} elseif (is_archive()) {
$page_type = 'archive';
} elseif (is_search()) {
$page_type = 'search-results';
} elseif (is_404()) {
$page_type = 'error-404';
}
// User status
$user_status = is_user_logged_in() ? 'logged-in' : 'logged-out';
$user_role = 'guest';
if (is_user_logged_in()) {
$user = wp_get_current_user();
$user_role = !empty($user->roles) ? $user->roles[0] : 'subscriber';
}
// Build data layer
$data_layer = array(
'pageType' => $page_type,
'pageName' => wp_get_document_title(),
'userStatus' => $user_status,
'userRole' => $user_role,
'language' => get_bloginfo('language'),
'siteEnvironment' => defined('WP_ENVIRONMENT_TYPE') ? WP_ENVIRONMENT_TYPE : 'production'
);
?>
<script>
dataLayer.push(<?php echo json_encode($data_layer); ?>);
</script>
<?php
}
Blog Post Data Layer
Enhanced data for blog posts and articles:
add_action('wp_head', 'blog_post_data_layer');
function blog_post_data_layer() {
if (!is_single()) {
return;
}
global $post;
// Get post categories and tags
$categories = wp_get_post_categories($post->ID, array('fields' => 'names'));
$tags = wp_get_post_tags($post->ID, array('fields' => 'names'));
// Calculate reading time
$word_count = str_word_count(strip_tags($post->post_content));
$reading_time = ceil($word_count / 200); // 200 words per minute
// Get author info
$author_id = $post->post_author;
$author_name = get_the_author_meta('display_name', $author_id);
// Get post age
$publish_date = get_the_date('Y-m-d', $post);
$days_since_publish = floor((time() - strtotime($post->post_date)) / (60 * 60 * 24));
$post_data = array(
'event' => 'blogPostView',
'pageType' => 'blog-post',
'postID' => $post->ID,
'postTitle' => get_the_title($post),
'postAuthor' => $author_name,
'postAuthorID' => $author_id,
'postCategories' => implode('|', $categories),
'postTags' => implode('|', $tags),
'publishDate' => $publish_date,
'daysSincePublish' => $days_since_publish,
'wordCount' => $word_count,
'readingTimeMinutes' => $reading_time,
'commentCount' => get_comments_number($post),
'hasComments' => get_comments_number($post) > 0 ? 'yes' : 'no'
);
// Add custom fields if present
$featured = get_post_meta($post->ID, '_featured_post', true);
if ($featured) {
$post_data['featuredPost'] = 'yes';
}
?>
<script>
dataLayer.push(<?php echo json_encode($post_data); ?>);
</script>
<?php
}
WooCommerce Data Layer
Product Page Data Layer
add_action('wp_footer', 'woocommerce_product_data_layer');
function woocommerce_product_data_layer() {
if (!function_exists('is_product') || !is_product()) {
return;
}
global $product;
// Get product categories
$categories = wp_get_post_terms($product->get_id(), 'product_cat', array('fields' => 'names'));
// Get product attributes
$attributes = array();
foreach ($product->get_attributes() as $attr_name => $attr) {
$attributes[] = wc_attribute_label($attr_name);
}
// Check stock status
$stock_status = $product->is_in_stock() ? 'in-stock' : 'out-of-stock';
$stock_quantity = $product->get_stock_quantity();
// Get price information
$regular_price = $product->get_regular_price();
$sale_price = $product->get_sale_price();
$on_sale = $product->is_on_sale();
$product_data = array(
'event' => 'productView',
'ecommerce' => array(
'detail' => array(
'products' => array(
array(
'id' => $product->get_sku() ?: $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price(),
'brand' => get_post_meta($product->get_id(), '_brand', true),
'category' => implode('/', $categories),
'variant' => $product->is_type('variable') ? 'variable' : 'simple',
'stockStatus' => $stock_status,
'stockQuantity' => $stock_quantity,
'onSale' => $on_sale ? 'yes' : 'no'
)
)
)
),
'productID' => $product->get_id(),
'productName' => $product->get_name(),
'productType' => $product->get_type(),
'productPrice' => $product->get_price(),
'productSKU' => $product->get_sku(),
'productCategories' => implode('|', $categories),
'productAttributes' => implode('|', $attributes)
);
?>
<script>
dataLayer.push(<?php echo json_encode($product_data); ?>);
</script>
<?php
}
Cart Data Layer
add_action('wp_footer', 'woocommerce_cart_data_layer');
function woocommerce_cart_data_layer() {
if (!function_exists('is_cart') || !is_cart()) {
return;
}
$cart = WC()->cart;
$cart_items = array();
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
$product = $cart_item['data'];
$cart_items[] = array(
'id' => $product->get_sku() ?: $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price(),
'quantity' => $cart_item['quantity']
);
}
$cart_data = array(
'event' => 'cartView',
'pageType' => 'cart',
'cartValue' => floatval($cart->get_cart_contents_total()),
'cartQuantity' => $cart->get_cart_contents_count(),
'cartItemCount' => count($cart->get_cart()),
'hasCoupon' => !empty($cart->get_applied_coupons()) ? 'yes' : 'no',
'appliedCoupons' => implode('|', $cart->get_applied_coupons()),
'ecommerce' => array(
'currencyCode' => get_woocommerce_currency(),
'cart' => array(
'products' => $cart_items
)
)
);
?>
<script>
dataLayer.push(<?php echo json_encode($cart_data); ?>);
</script>
<?php
}
Checkout Data Layer
add_action('woocommerce_before_checkout_form', 'woocommerce_checkout_data_layer');
function woocommerce_checkout_data_layer() {
$cart = WC()->cart;
$checkout_items = array();
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
$product = $cart_item['data'];
$checkout_items[] = array(
'id' => $product->get_sku() ?: $product->get_id(),
'name' => $product->get_name(),
'price' => $product->get_price(),
'category' => wp_strip_all_tags(wc_get_product_category_list($product->get_id())),
'quantity' => $cart_item['quantity']
);
}
$checkout_data = array(
'event' => 'checkoutView',
'pageType' => 'checkout',
'checkoutStep' => 1,
'checkoutValue' => floatval($cart->get_cart_contents_total()),
'checkoutTax' => floatval($cart->get_cart_contents_tax()),
'checkoutShipping' => floatval($cart->get_shipping_total()),
'checkoutTotal' => floatval($cart->get_total('')),
'ecommerce' => array(
'checkout' => array(
'actionField' => array(
'step' => 1,
'option' => ''
),
'products' => $checkout_items
)
)
);
?>
<script>
dataLayer.push(<?php echo json_encode($checkout_data); ?>);
</script>
<?php
}
Purchase Confirmation Data Layer
add_action('woocommerce_thankyou', 'woocommerce_purchase_data_layer');
function woocommerce_purchase_data_layer($order_id) {
if (!$order_id) {
return;
}
// Prevent duplicate tracking
$tracked = get_post_meta($order_id, '_gtm_datalayer_tracked', true);
if ($tracked) {
return;
}
$order = wc_get_order($order_id);
$order_items = array();
foreach ($order->get_items() as $item_id => $item) {
$product = $item->get_product();
$order_items[] = array(
'id' => $product->get_sku() ?: $product->get_id(),
'name' => $item->get_name(),
'price' => $item->get_total() / $item->get_quantity(),
'category' => wp_strip_all_tags(wc_get_product_category_list($product->get_id())),
'quantity' => $item->get_quantity()
);
}
$purchase_data = array(
'event' => 'purchase',
'pageType' => 'order-confirmation',
'transactionId' => $order->get_order_number(),
'transactionTotal' => floatval($order->get_total()),
'transactionTax' => floatval($order->get_total_tax()),
'transactionShipping' => floatval($order->get_shipping_total()),
'transactionCurrency' => $order->get_currency(),
'transactionPaymentMethod' => $order->get_payment_method_title(),
'ecommerce' => array(
'purchase' => array(
'actionField' => array(
'id' => $order->get_order_number(),
'revenue' => floatval($order->get_total()),
'tax' => floatval($order->get_total_tax()),
'shipping' => floatval($order->get_shipping_total()),
'coupon' => implode('|', $order->get_coupon_codes())
),
'products' => $order_items
)
)
);
?>
<script>
dataLayer.push(<?php echo json_encode($purchase_data); ?>);
</script>
<?php
// Mark as tracked
update_post_meta($order_id, '_gtm_datalayer_tracked', 'yes');
}
Page Builder Data Layers
Elementor Data Layer
add_action('wp_footer', 'elementor_data_layer');
function elementor_data_layer() {
if (!did_action('elementor/loaded')) {
return;
}
global $post;
// Check if page is built with Elementor
$is_elementor = get_post_meta($post->ID, '_elementor_edit_mode', true);
if ($is_elementor === 'builder') {
?>
<script>
dataLayer.push({
'pageBuilder': 'elementor',
'elementorVersion': '<?php echo ELEMENTOR_VERSION; ?>',
'isElementorPage': 'yes'
});
</script>
<?php
}
}
Divi Data Layer
add_action('wp_footer', 'divi_data_layer');
function divi_data_layer() {
if (!function_exists('et_divi_builder_init_plugin')) {
return;
}
global $post;
// Check if Divi Builder is enabled
$divi_enabled = get_post_meta($post->ID, '_et_pb_use_builder', true);
if ($divi_enabled === 'on') {
?>
<script>
dataLayer.push({
'pageBuilder': 'divi',
'isDiviPage': 'yes'
});
</script>
<?php
}
}
User-Based Data Layer
Membership and Roles
add_action('wp_head', 'user_membership_data_layer');
function user_membership_data_layer() {
if (!is_user_logged_in()) {
return;
}
$user = wp_get_current_user();
$user_role = !empty($user->roles) ? $user->roles[0] : 'subscriber';
// Calculate user account age
$registration_date = get_userdata($user->ID)->user_registered;
$account_age_days = floor((time() - strtotime($registration_date)) / (60 * 60 * 24));
// Check for WooCommerce customer data
$total_orders = 0;
$lifetime_value = 0;
if (function_exists('wc_get_customer_order_count')) {
$total_orders = wc_get_customer_order_count($user->ID);
$lifetime_value = wc_get_customer_total_spent($user->ID);
}
// Customer segment
$customer_segment = 'new';
if ($total_orders > 0) {
$customer_segment = 'returning';
}
if ($total_orders > 5) {
$customer_segment = 'loyal';
}
if ($lifetime_value > 1000) {
$customer_segment = 'vip';
}
$user_data = array(
'userID' => $user->ID,
'userEmail' => hash('sha256', $user->user_email), // Hashed for privacy
'userRole' => $user_role,
'accountAgeDays' => $account_age_days,
'totalOrders' => $total_orders,
'lifetimeValue' => $lifetime_value,
'customerSegment' => $customer_segment
);
?>
<script>
dataLayer.push(<?php echo json_encode($user_data); ?>);
</script>
<?php
}
Event-Based Data Layer Pushes
Form Submission Events
add_action('wp_footer', 'form_submission_data_layer');
function form_submission_data_layer() {
?>
<script>
// Contact Form 7
document.addEventListener('wpcf7mailsent', function(event) {
dataLayer.push({
'event': 'formSubmission',
'formType': 'contact-form-7',
'formID': event.detail.contactFormId,
'formName': event.detail.unitTag
});
});
// Gravity Forms
jQuery(document).on('gform_confirmation_loaded', function(event, formId) {
dataLayer.push({
'event': 'formSubmission',
'formType': 'gravity-forms',
'formID': formId
});
});
// WPForms
jQuery(document).on('wpformsAjaxSubmitSuccess', function(event, data) {
dataLayer.push({
'event': 'formSubmission',
'formType': 'wpforms',
'formID': data.form_id
});
});
</script>
<?php
}
WooCommerce Add-to-Cart Events
add_action('wp_footer', 'add_to_cart_data_layer_event');
function add_to_cart_data_layer_event() {
if (!is_product()) {
return;
}
global $product;
?>
<script>
jQuery(document).ready(function($) {
$('.single_add_to_cart_button').on('click', function() {
const quantity = parseInt($('.qty').val() || 1);
const price = parseFloat('<?php echo $product->get_price(); ?>');
dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'add': {
'products': [{
'id': '<?php echo esc_js($product->get_sku() ?: $product->get_id()); ?>',
'name': '<?php echo esc_js($product->get_name()); ?>',
'price': price,
'quantity': quantity
}]
}
}
});
});
});
</script>
<?php
}
Custom Dimensions and Metrics
WordPress-Specific Custom Dimensions
add_action('wp_head', 'custom_dimensions_data_layer');
function custom_dimensions_data_layer() {
global $post;
$custom_data = array();
// Site-specific data
$custom_data['siteID'] = get_current_blog_id(); // For multisite
$custom_data['themeVersion'] = wp_get_theme()->get('Version');
$custom_data['wpVersion'] = get_bloginfo('version');
// Content age segmentation
if (isset($post->post_date)) {
$content_age_days = floor((time() - strtotime($post->post_date)) / (60 * 60 * 24));
if ($content_age_days <= 7) {
$custom_data['contentAge'] = 'new';
} elseif ($content_age_days <= 30) {
$custom_data['contentAge'] = 'recent';
} elseif ($content_age_days <= 180) {
$custom_data['contentAge'] = 'medium';
} else {
$custom_data['contentAge'] = 'old';
}
}
// Traffic source context
if (isset($_SERVER['HTTP_REFERER'])) {
$referrer = $_SERVER['HTTP_REFERER'];
if (strpos($referrer, 'google.com') !== false) {
$custom_data['trafficSource'] = 'google';
} elseif (strpos($referrer, 'facebook.com') !== false) {
$custom_data['trafficSource'] = 'facebook';
}
}
?>
<script>
dataLayer.push(<?php echo json_encode($custom_data); ?>);
</script>
<?php
}
Accessing Data Layer in GTM
Create Data Layer Variables
In GTM:
- Variables → New → Data Layer Variable
- Name: Page Type
- Data Layer Variable Name: pageType
- Save
Repeat for all data layer keys (postID, productName, userRole, etc.)
Use in Tags
- Event Name: blog_post_view
- Event Parameters:
- post_id:
\{\{DLV - postID\}\} - post_author:
\{\{DLV - postAuthor\}\} - post_category:
\{\{DLV - postCategories\}\}
- post_id:
Use in Triggers
Example trigger for logged-in users:
- Trigger Type: Page View - DOM Ready
- Condition:
\{\{DLV - userStatus\}\}equalslogged-in
Debugging the Data Layer
Console Inspection
// View entire dataLayer
console.table(dataLayer);
// Watch for new pushes
const originalPush = dataLayer.push;
dataLayer.push = function() {
console.log('dataLayer.push:', arguments[0]);
return originalPush.apply(this, arguments);
};
GTM Preview Mode
- Open GTM → Preview
- Enter WordPress site URL
- Click Variables tab
- Verify all Data Layer Variables populate correctly
DataLayer Checker Chrome Extension
Install dataslayer to visualize data layer in real-time.
Performance Considerations
Lazy Load Data Layer for Non-Critical Pages
// Only build complex data layer on pages that need it
add_action('wp_head', 'conditional_data_layer');
function conditional_data_layer() {
// Full data layer for product/checkout pages
if (is_product() || is_checkout() || is_cart()) {
woocommerce_full_data_layer();
} else {
// Minimal data layer for blog posts
basic_wordpress_data_layer();
}
}
Next Steps
Related Resources
- GTM Data Layer Fundamentals - Universal concepts
- GTM Setup Guide - Install GTM on WordPress
- WordPress Integrations - Overview of all tracking options