Hotjar Data Layer Setup | OpsBlu Docs

Hotjar Data Layer Setup

Configure Google Tag Manager data layer integration for Hotjar event tracking and user identification.

Overview

The data layer is a JavaScript object that stores information about user interactions, page data, and application state. When using Google Tag Manager (GTM), the data layer becomes the bridge between your website and Hotjar, allowing you to trigger events and pass user attributes without hardcoding tracking logic.

Benefits of using a data layer with Hotjar:

  • Centralized data management
  • No code deployments for tracking changes
  • Easier testing and debugging
  • Consistent data structure across tools
  • Better collaboration between dev and marketing teams

Data Layer Fundamentals

What is the Data Layer?

The data layer is a global JavaScript array (dataLayer) that holds data about the page, user, and events:

window.dataLayer = window.dataLayer || [];

dataLayer.push({
    'event': 'page_view',
    'page_type': 'homepage',
    'user_status': 'logged_in'
});

GTM listens to the data layer and fires tags (including Hotjar events) based on what gets pushed.

Data Layer vs Direct Hotjar Calls

Without Data Layer (Direct):

// Hardcoded in your site
hj('event', 'signup_completed');

With Data Layer (GTM):

// Push to data layer
dataLayer.push({
    'event': 'signup_completed'
});

// GTM handles calling Hotjar
// No need to touch hj() directly

The GTM approach is more flexible and maintainable.

Initial Data Layer Setup

Prerequisites

  • Google Tag Manager installed on your site
  • Hotjar tracking configured in GTM (see Install Guide)

Basic Structure

Initialize the data layer before GTM loads:

<!DOCTYPE html>
<html>
<head>
    <title>Your Page</title>

    <!-- Initialize Data Layer -->
    <script>
        window.dataLayer = window.dataLayer || [];

        // Push page-level data
        dataLayer.push({
            'page_type': 'homepage',
            'content_category': 'marketing',
            'user_login_status': 'logged_out'
        });
    </script>

    <!-- Google Tag Manager -->
    <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
    <!-- End Google Tag Manager -->
</head>
<body>
    <!-- Your content -->
</body>
</html>

Important: Initialize dataLayer before the GTM script loads.

Page-Level Data

Push data about the page when it loads:

Homepage Example

dataLayer.push({
    'page_type': 'homepage',
    'page_category': 'marketing',
    'content_group': 'top_funnel',
    'logged_in': false
});

Product Page Example

dataLayer.push({
    'page_type': 'product',
    'product_id': 'SKU-12345',
    'product_name': 'Blue Widget',
    'product_price': 29.99,
    'product_category': 'Widgets',
    'in_stock': true
});

Checkout Page Example

dataLayer.push({
    'page_type': 'checkout',
    'checkout_step': 'payment',
    'cart_value': 149.97,
    'cart_items': 3
});

Event Tracking via Data Layer

Basic Event Push

To track an event, push an object with an event key:

dataLayer.push({
    'event': 'button_click',
    'button_name': 'signup_cta',
    'button_location': 'homepage_hero'
});

Form Events

Form Start:

dataLayer.push({
    'event': 'form_start',
    'form_name': 'contact_us',
    'form_location': 'footer'
});

Form Submit:

dataLayer.push({
    'event': 'form_submit',
    'form_name': 'contact_us',
    'form_fields': 5,
    'form_completion_time': 45 // seconds
});

Form Error:

dataLayer.push({
    'event': 'form_error',
    'form_name': 'contact_us',
    'error_field': 'email',
    'error_type': 'invalid_format'
});

E-commerce Events

Add to Cart:

dataLayer.push({
    'event': 'add_to_cart',
    'product_id': 'SKU-12345',
    'product_name': 'Blue Widget',
    'product_price': 29.99,
    'quantity': 1
});

Checkout Started:

dataLayer.push({
    'event': 'begin_checkout',
    'cart_value': 149.97,
    'cart_items': 3
});

Purchase Completed:

dataLayer.push({
    'event': 'purchase',
    'transaction_id': 'TXN-789',
    'revenue': 149.97,
    'tax': 12.00,
    'shipping': 5.99
});

User Interaction Events

Video Play:

dataLayer.push({
    'event': 'video_play',
    'video_title': 'Product Demo 2024',
    'video_duration': 120,
    'video_provider': 'youtube'
});

File Download:

dataLayer.push({
    'event': 'file_download',
    'file_name': 'pricing-guide.pdf',
    'file_type': 'pdf',
    'file_size': '2.4MB'
});

CTA Click:

dataLayer.push({
    'event': 'cta_click',
    'cta_text': 'Start Free Trial',
    'cta_location': 'pricing_page',
    'cta_type': 'button'
});

User Identification via Data Layer

Logged-In User

When a user logs in, push their attributes to the data layer:

// On successful login
dataLayer.push({
    'event': 'user_login',
    'user_id': 'user_12345',
    'user_email': 'user@example.com',
    'user_plan': 'premium',
    'user_signup_date': '2024-01-15',
    'user_role': 'admin'
});

User Attributes

For ongoing pages (after login), include user data in the initial page load:

dataLayer.push({
    'user_id': 'user_12345',
    'user_plan': 'premium',
    'user_account_age_days': 120,
    'user_lifetime_value': 599.99,
    'user_segment': 'power_user'
});

Single Page Applications (SPAs)

For SPAs, push user data after each route change:

// On route change
dataLayer.push({
    'event': 'virtual_pageview',
    'page_path': '/dashboard',
    'page_title': 'User Dashboard',
    'user_id': currentUser.id,
    'user_plan': currentUser.plan
});

Configuring GTM to Trigger Hotjar Events

Now that data is flowing into the data layer, configure GTM to send it to Hotjar.

Step 1: Create Data Layer Variables

In GTM, create variables to capture data layer values:

  1. Go to Variables > New
  2. Click Variable Configuration
  3. Choose Data Layer Variable
  4. Set Data Layer Variable Name (e.g., event, button_name, user_id)
  5. Save

Example Variables:

  • Variable Name: DLV - Event | Data Layer Variable Name: event
  • Variable Name: DLV - Button Name | Data Layer Variable Name: button_name
  • Variable Name: DLV - User ID | Data Layer Variable Name: user_id

Step 2: Create Custom Event Trigger

  1. Go to Triggers > New
  2. Choose Trigger Type: Custom Event
  3. Set Event name to match your data layer event (e.g., button_click)
  4. Save

Step 3: Create Hotjar Event Tag

  1. Go to Tags > New
  2. Choose Tag Type: Custom HTML
  3. Add the code:
<script>
  if (window.hj) {
    hj('event', {{DLV - Event}});
  }
</script>
  1. Set Triggering to your custom event trigger (e.g., button_click)
  2. Save

Step 4: Test in Preview Mode

  1. Click Preview in GTM
  2. Navigate to your site
  3. Trigger the event (e.g., click a button)
  4. In GTM Debug, verify:
    • Data layer push appears
    • Trigger fires
    • Hotjar tag executes
  5. Check browser console: localStorage.setItem('hjDebug', 'true') to see Hotjar events

Step 5: Publish

Once validated, publish your GTM container.

Advanced Data Layer Patterns

Dynamic Event Names

If you want Hotjar event names to be dynamic:

// Data layer push
dataLayer.push({
    'event': 'user_action',
    'action_name': 'signup_completed'
});

GTM Tag:

<script>
  if (window.hj && {{DLV - Action Name}}) {
    hj('event', {{DLV - Action Name}});
  }
</script>

Conditional Event Firing

Only fire Hotjar events for specific user segments:

Data Layer:

dataLayer.push({
    'event': 'feature_used',
    'feature_name': 'export_data',
    'user_plan': 'premium'
});

GTM Trigger:

  • Trigger Type: Custom Event
  • Event name: feature_used
  • This trigger fires on: Some Custom Events
  • Fire when: {{DLV - User Plan}} equals premium

User Identification via GTM

Send user attributes to Hotjar's Identify API:

Data Layer Push:

dataLayer.push({
    'event': 'user_identified',
    'user_id': 'user_12345',
    'user_email': 'user@example.com',
    'user_plan': 'premium',
    'user_signup_date': '2024-01-15'
});

GTM Tag (Custom HTML):

<script>
  if (window.hj && {{DLV - User ID}}) {
    hj('identify', {{DLV - User ID}}, {
      'email': {{DLV - User Email}},
      'plan': {{DLV - User Plan}},
      'signup_date': {{DLV - User Signup Date}}
    });
  }
</script>

Trigger: Custom Event user_identified

Single Page Application (SPA) Data Layer

Virtual Pageviews

For SPAs, push a virtual pageview on route changes:

// React Router example
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function useDataLayerPageview() {
    const location = useLocation();

    useEffect(() => {
        window.dataLayer = window.dataLayer || [];
        dataLayer.push({
            'event': 'virtual_pageview',
            'page_path': location.pathname,
            'page_title': document.title
        });
    }, [location]);
}

GTM Configuration:

  1. Trigger: Custom Event virtual_pageview
  2. Tag: Hotjar state change
<script>
  if (window.hj) {
    hj('stateChange', {{DLV - Page Path}});
  }
</script>

SPA Event Tracking

// Vue example
methods: {
    trackButtonClick(buttonName) {
        dataLayer.push({
            'event': 'button_click',
            'button_name': buttonName,
            'page_path': this.$route.path
        });
    }
}

Data Layer Best Practices

Naming Conventions

Use consistent naming:

  • snake_case for event names: form_submit, video_play
  • Descriptive keys: product_name not pn
  • Prefixes for clarity: user_plan, page_type

Avoid:

  • Inconsistent casing: formSubmit vs form_submit
  • Ambiguous keys: type, name, data
  • Special characters: form-name, user.id

Data Structure

Keep it flat:

dataLayer.push({
    'event': 'purchase',
    'transaction_id': 'TXN-123',
    'revenue': 99.99
});

Avoid nested objects (harder to access in GTM):

dataLayer.push({
    'event': 'purchase',
    'transaction': {
        'id': 'TXN-123',
        'revenue': 99.99
    }
});

Timing

Push before GTM loads (for page-level data):

<script>
    window.dataLayer = window.dataLayer || [];
    dataLayer.push({...});
</script>
<!-- GTM script here -->

Push after user action (for events):

button.addEventListener('click', () => {
    dataLayer.push({...});
});

Privacy & Compliance

Do:

  • Use user IDs, not emails in event names
  • Hash or encrypt PII before pushing
  • Respect user consent before tracking
  • Document what data you collect

Don't:

  • Push credit card numbers, passwords
  • Include raw email addresses in events
  • Track without user consent
  • Push unnecessary sensitive data

Debugging the Data Layer

Chrome Extension: dataLayer Checker

Install dataLayer Inspector Chrome extension to:

  • View data layer pushes in real-time
  • Inspect variable values
  • Debug GTM triggers and tags

Console Commands

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

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

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

GTM Preview Mode

  1. Enable Preview in GTM
  2. Navigate your site
  3. In Debug panel:
    • Summary: See which tags fired
    • Variables: Check data layer variable values
    • Data Layer: View all pushes in chronological order

Common Issues

Event not firing in Hotjar:

  • Check data layer push appears in GTM Debug
  • Verify trigger conditions met
  • Confirm Hotjar tag fires
  • Enable hjDebug and check console

Variable undefined:

  • Ensure data layer key matches variable name exactly
  • Check timing (variable might not exist yet)
  • Verify data layer push happened before tag fires

Example Implementation

Complete E-commerce Flow

Product Page:

dataLayer.push({
    'page_type': 'product',
    'product_id': 'SKU-12345',
    'product_name': 'Blue Widget',
    'product_price': 29.99,
    'product_category': 'Widgets',
    'user_id': currentUser ? currentUser.id : null,
    'user_plan': currentUser ? currentUser.plan : 'guest'
});

Add to Cart Click:

addToCartButton.addEventListener('click', () => {
    dataLayer.push({
        'event': 'add_to_cart',
        'product_id': 'SKU-12345',
        'product_name': 'Blue Widget',
        'product_price': 29.99,
        'quantity': 1
    });
});

Checkout Initiated:

checkoutButton.addEventListener('click', () => {
    dataLayer.push({
        'event': 'begin_checkout',
        'cart_value': 149.97,
        'cart_items': 3,
        'user_id': currentUser.id
    });
});

Purchase Completed:

// On thank you page
dataLayer.push({
    'event': 'purchase',
    'transaction_id': 'TXN-789',
    'revenue': 149.97,
    'tax': 12.00,
    'shipping': 5.99,
    'user_id': currentUser.id
});

GTM Setup:

Create triggers and Hotjar event tags for:

  • add_to_carthj('event', 'add_to_cart')
  • begin_checkouthj('event', 'checkout_started')
  • purchasehj('event', 'purchase_completed')

Next Steps:

Additional Resources: