WordPress Data Layer for GTM | Blue Frog Docs

WordPress Data Layer for GTM

Build a comprehensive data layer for WordPress including WooCommerce, page builders, and custom content

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:

  1. Variables → New → Data Layer Variable
  2. Name: Page Type
  3. Data Layer Variable Name: pageType
  4. Save

Repeat for all data layer keys (postID, productName, userRole, etc.)

Use in Tags

Example GA4 Event Tag:

  • Event Name: blog_post_view
  • Event Parameters:
    • post_id: \{\{DLV - postID\}\}
    • post_author: \{\{DLV - postAuthor\}\}
    • post_category: \{\{DLV - postCategories\}\}

Use in Triggers

Example trigger for logged-in users:

  • Trigger Type: Page View - DOM Ready
  • Condition: \{\{DLV - userStatus\}\} equals logged-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

  1. Open GTM → Preview
  2. Enter WordPress site URL
  3. Click Variables tab
  4. 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

// SYS.FOOTER