Hotjar Event Tracking Setup | OpsBlu Docs

Hotjar Event Tracking Setup

Implementation guide for setting up custom events using Hotjar Events API and Google Tag Manager.

Setup Overview

Event tracking in Hotjar requires both client-side implementation and configuration in the Hotjar dashboard. This guide covers the complete setup process from defining your event taxonomy to validating that events are firing correctly.

Implementation Planning

Before writing code, plan your event tracking strategy.

Define Your Event Taxonomy

Create a structured list of events you want to track:

Event Category Event Name Purpose Trigger
User Actions signup_button_clicked Track signup intent Button click
Forms checkout_form_started Monitor form engagement First field focus
Forms checkout_form_submitted Measure form completion Form submit
E-commerce add_to_cart Track product interest Add to cart button
E-commerce purchase_completed Measure conversions Thank you page load
Content video_played Understand engagement Video play button
Errors payment_failed Identify friction Error state

Event Naming Convention

Establish a consistent naming pattern:

Recommended Format:

[category]_[action]_[object]

Examples:
- form_submitted_checkout
- button_clicked_signup
- video_played_product_demo
- error_displayed_payment

Guidelines:

  • Use snake_case
  • Be descriptive but concise
  • Group related events with prefixes
  • Avoid dynamic values in names

Implementation Methods

Method 1: Direct JavaScript Implementation

Best for developers with direct access to site code.

Basic Event Tracking

// Wait for Hotjar to load
function trackHotjarEvent(eventName) {
    if (typeof hj !== 'undefined') {
        hj('event', eventName);
    } else {
        console.warn('Hotjar not loaded, event not tracked:', eventName);
    }
}

// Track button click
document.getElementById('signup-button').addEventListener('click', function() {
    trackHotjarEvent('signup_button_clicked');
});

Form Event Tracking

const form = document.querySelector('#checkout-form');

// Track form start (first interaction)
form.addEventListener('focus', function(e) {
    if (e.target.matches('input, select, textarea') && !form.dataset.hjStarted) {
        form.dataset.hjStarted = 'true';
        trackHotjarEvent('checkout_form_started');
    }
}, true);

// Track form submission
form.addEventListener('submit', function(e) {
    trackHotjarEvent('checkout_form_submitted');
});

// Track form abandonment
let formStarted = false;

form.addEventListener('focus', function(e) {
    if (e.target.matches('input, select, textarea')) {
        formStarted = true;
    }
}, true);

window.addEventListener('beforeunload', function() {
    if (formStarted && !form.dataset.submitted) {
        trackHotjarEvent('checkout_form_abandoned');
    }
});

form.addEventListener('submit', function() {
    form.dataset.submitted = 'true';
});

E-commerce Event Tracking

// Add to cart
function addToCart(productId, productName) {
    // Your add-to-cart logic here

    trackHotjarEvent('add_to_cart');

    console.log('Product added:', productName);
}

// Remove from cart
function removeFromCart(productId) {
    // Your remove-from-cart logic here

    trackHotjarEvent('remove_from_cart');
}

// Checkout started
if (window.location.pathname === '/checkout') {
    trackHotjarEvent('checkout_started');
}

// Purchase completed
if (window.location.pathname === '/thank-you') {
    trackHotjarEvent('purchase_completed');
}

Video Tracking

const video = document.getElementById('product-video');

// Video play
video.addEventListener('play', function() {
    trackHotjarEvent('video_played_product_demo');
});

// Video completion (watched 90%+)
video.addEventListener('timeupdate', function() {
    const percentWatched = (video.currentTime / video.duration) * 100;

    if (percentWatched >= 90 && !video.dataset.hjCompleted) {
        video.dataset.hjCompleted = 'true';
        trackHotjarEvent('video_completed_product_demo');
    }
});

Error Tracking

// Track JavaScript errors
window.addEventListener('error', function(e) {
    trackHotjarEvent('javascript_error_occurred');
    console.error('Error tracked:', e.message);
});

// Track form validation errors
function showFormError(fieldName) {
    trackHotjarEvent('form_validation_error');
    console.log('Validation error on:', fieldName);
}

// Track API errors
fetch('/api/checkout')
    .then(response => {
        if (!response.ok) {
            trackHotjarEvent('api_error_checkout');
        }
        return response.json();
    })
    .catch(error => {
        trackHotjarEvent('api_error_checkout');
        console.error('API error:', error);
    });

Method 2: Google Tag Manager Implementation

Best for teams that prefer managing tracking via GTM.

Setup Overview

  1. Push events to data layer
  2. Create GTM triggers for each event
  3. Create Hotjar event tags
  4. Test and publish

Data Layer Implementation

// Generic event tracking function
function trackEvent(eventName, eventData = {}) {
    window.dataLayer = window.dataLayer || [];
    dataLayer.push({
        'event': eventName,
        ...eventData
    });
}

// Track button click
document.getElementById('signup-button').addEventListener('click', function() {
    trackEvent('signup_button_clicked', {
        'button_location': 'homepage_hero',
        'button_text': this.textContent
    });
});

// Track form submission
document.querySelector('#checkout-form').addEventListener('submit', function() {
    trackEvent('checkout_form_submitted', {
        'form_fields': this.querySelectorAll('input').length
    });
});

// Track add to cart
function addToCart(product) {
    trackEvent('add_to_cart', {
        'product_id': product.id,
        'product_name': product.name,
        'product_price': product.price
    });
}

GTM Configuration

Step 1: Create Trigger

  1. In GTM, go to Triggers > New
  2. Trigger Type: Custom Event
  3. Event name: signup_button_clicked (matches data layer event)
  4. Save

Step 2: Create Hotjar Event Tag

  1. Go to Tags > New
  2. Tag Type: Custom HTML
  3. Code:
<script>
  if (window.hj) {
    hj('event', 'signup_button_clicked');
  }
</script>
  1. Triggering: Select your custom event trigger
  2. Save

Step 3: Test

  1. Click Preview in GTM
  2. Navigate to your site
  3. Trigger the event (click the button)
  4. Verify in GTM Debug that tag fires
  5. Check browser console for Hotjar confirmation

Step 4: Publish

Once validated, publish your GTM container.

Method 3: Segment Integration

Best for teams already using Segment.

Implementation

// Segment handles routing to Hotjar automatically
analytics.track('Signup Button Clicked', {
    location: 'homepage',
    button_text: 'Start Free Trial'
});

// Translates to:
// hj('event', 'Signup Button Clicked');

Note: Event properties are not passed to Hotjar. Use descriptive event names.

Single Page Application (SPA) Setup

React Implementation

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

// Custom hook for tracking events
export function useHotjarEvent() {
    const trackEvent = (eventName) => {
        if (window.hj) {
            window.hj('event', eventName);
        }
    };

    return trackEvent;
}

// Custom hook for page tracking
export function useHotjarPageTracking() {
    const location = useLocation();

    useEffect(() => {
        if (window.hj) {
            window.hj('stateChange', location.pathname);
        }
    }, [location]);
}

// Usage in component
function SignupButton() {
    const trackEvent = useHotjarEvent();

    const handleClick = () => {
        trackEvent('signup_button_clicked');
        // Your signup logic
    };

    return <button Up</button>;
}

// Usage in App
function App() {
    useHotjarPageTracking();

    return (
        <Router>
            {/* Your routes */}
        </Router>
    );
}

Vue.js Implementation

// In your Vue component
export default {
    methods: {
        trackEvent(eventName) {
            if (window.hj) {
                window.hj('event', eventName);
            }
        },

        handleSignupClick() {
            this.trackEvent('signup_button_clicked');
            // Your signup logic
        }
    }
};

// In your router
router.afterEach((to) => {
    if (window.hj) {
        window.hj('stateChange', to.path);
    }
});

Angular Implementation

// Event tracking service
import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class HotjarService {
    trackEvent(eventName: string): void {
        if (window['hj']) {
            window['hj']('event', eventName);
        }
    }

    trackPageview(path: string): void {
        if (window['hj']) {
            window['hj']('stateChange', path);
        }
    }
}

// Usage in component
import { Component } from '@angular/core';
import { HotjarService } from './hotjar.service';

@Component({
    selector: 'app-signup',
    template: `<button (click)="handleSignup()">Sign Up</button>`
})
export class SignupComponent {
    constructor(private hotjar: HotjarService) {}

    handleSignup(): void {
        this.hotjar.trackEvent('signup_button_clicked');
        // Your signup logic
    }
}

// Route tracking in app component
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

export class AppComponent {
    constructor(
        private router: Router,
        private hotjar: HotjarService
    ) {
        this.router.events.pipe(
            filter(event => event instanceof NavigationEnd)
        ).subscribe((event: NavigationEnd) => {
            this.hotjar.trackPageview(event.urlAfterRedirects);
        });
    }
}

Event Validation & Testing

Pre-Production Testing

Staging Environment Checklist:

  • Event fires on expected user action
  • Event name is correct
  • No JavaScript errors in console
  • Hotjar debug mode shows event
  • Event appears in Hotjar dashboard (may take a few minutes)
  • Event can be used as recording filter
  • Event can trigger surveys
  • Works across browsers (Chrome, Firefox, Safari, Edge)
  • Works on mobile devices

Browser Console Testing

// Enable Hotjar debug mode
localStorage.setItem('hjDebug', 'true');

// Refresh page

// Trigger your event
hj('event', 'test_event');

// Check console output:
// "Hotjar: event tracked: test_event"

Network Monitoring

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Filter by "hotjar"
  4. Trigger your event
  5. Look for request to insights.hotjar.com/api/v2/events
  6. Inspect payload to verify event name

GTM Preview Mode

  1. Enable Preview in GTM
  2. Navigate to your site
  3. Trigger the event
  4. In GTM Debug panel:
    • Verify data layer push appears
    • Check that trigger fires
    • Confirm Hotjar tag executes
    • View Variables to see event data

Hotjar Dashboard Validation

  1. Go to Hotjar dashboard
  2. Navigate to Recordings
  3. Click Filters
  4. Add filter: Event > Select your event name
  5. Wait a few minutes for data to process
  6. Verify recordings appear with your event

Using Events in Hotjar

Filtering Recordings by Event

  1. Go to Recordings in Hotjar
  2. Click Filters button
  3. Select Event
  4. Choose the event name(s) you want to filter by
  5. Apply filter

Now you'll only see recordings where users triggered that specific event.

Triggering Recordings with Events

Configure recordings to only capture sessions where specific events occur:

  1. Go to Recordings > Create new or edit existing
  2. Under Advanced filters, select Track only sessions where:
  3. Choose Event happens
  4. Enter your event name
  5. Save

This helps conserve recording quota by only capturing relevant sessions.

Triggering Surveys with Events

Display surveys at key moments using events:

  1. Go to Surveys > Create new or edit existing
  2. Under Targeting, select When
  3. Choose Event happens
  4. Enter your event name
  5. Configure survey content and design
  6. Save and activate

Example use cases:

  • Show NPS survey after purchase_completed
  • Ask for feedback after onboarding_finished
  • Trigger exit survey when subscription_cancelled

Common Implementation Patterns

Scroll Depth Tracking

let scrollTracked = {
    25: false,
    50: false,
    75: false,
    100: false
};

window.addEventListener('scroll', function() {
    const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;

    Object.keys(scrollTracked).forEach(threshold => {
        if (scrollPercent >= parseInt(threshold) && !scrollTracked[threshold]) {
            scrollTracked[threshold] = true;
            trackHotjarEvent(`scroll_${threshold}_percent`);
        }
    });
});

Time on Page Tracking

// Track users who spend significant time on page
setTimeout(() => {
    trackHotjarEvent('spent_30_seconds_on_page');
}, 30000);

setTimeout(() => {
    trackHotjarEvent('spent_60_seconds_on_page');
}, 60000);

Exit Intent Tracking

let exitIntentFired = false;

document.addEventListener('mouseleave', function(e) {
    if (e.clientY < 0 && !exitIntentFired) {
        exitIntentFired = true;
        trackHotjarEvent('exit_intent_detected');
    }
});

Rage Click Detection

let clickCount = 0;
let clickTimer;

document.addEventListener('click', function(e) {
    clickCount++;

    clearTimeout(clickTimer);

    if (clickCount >= 3) {
        trackHotjarEvent('rage_click_detected');
        clickCount = 0;
    }

    clickTimer = setTimeout(() => {
        clickCount = 0;
    }, 1000);
});

Best Practices

Event Tracking

Do:

  • Track meaningful business actions
  • Use consistent naming conventions
  • Test events in staging first
  • Document your event taxonomy
  • Limit total number of unique events (< 50 recommended)
  • Use events to trigger recordings and surveys

Don't:

  • Track every possible interaction
  • Use dynamic values in event names
  • Forget to test cross-browser
  • Track without user consent
  • Create events you'll never analyze

Performance

Do:

  • Load Hotjar asynchronously
  • Throttle high-frequency events (scroll, mousemove)
  • Use event delegation for dynamic elements
  • Check if hj exists before calling

Don't:

  • Track events on every scroll/mousemove
  • Block user interactions waiting for events
  • Load Hotjar synchronously

Privacy

Do:

  • Respect user consent
  • Avoid PII in event names
  • Update privacy policy
  • Provide opt-out mechanism

Don't:

  • Track without consent
  • Include email/name in events
  • Ignore GDPR/CCPA requirements

Next Steps:

Additional Resources: