GA4 Event Tracking on Sitecore | Blue Frog Docs

GA4 Event Tracking on Sitecore

Implement custom event tracking for GA4 on Sitecore including form submissions, downloads, CTA clicks, and xDB integration

GA4 Event Tracking on Sitecore

Learn how to track custom events in Google Analytics 4 on Sitecore websites, from simple click tracking to advanced xDB integrations and Sitecore Forms.

Prerequisites

Event Tracking Methods

1. Basic JavaScript Events

Simple client-side tracking in Razor views:

@* Button click tracking *@
<button onclick="gtag('event', 'cta_click', {
    'event_category': 'CTA',
    'event_label': '@Model.Item["Button Text"]',
    'value': 1
});">
    @Model.Item["Button Text"]
</button>

2. Centralized Event Tracking Script

Create reusable tracking script:

// /scripts/analytics/sitecore-events.js
(function(window) {
    'use strict';

    window.SitecoreAnalytics = window.SitecoreAnalytics || {};

    // Track generic event
    SitecoreAnalytics.trackEvent = function(eventName, params) {
        if (typeof gtag === 'function') {
            gtag('event', eventName, params);
        }
    };

    // Track CTA clicks
    SitecoreAnalytics.trackCTA = function(ctaName, ctaLocation) {
        this.trackEvent('cta_click', {
            'cta_name': ctaName,
            'cta_location': ctaLocation,
            'page_path': window.location.pathname
        });
    };

    // Track downloads
    SitecoreAnalytics.trackDownload = function(fileName, fileType) {
        this.trackEvent('file_download', {
            'file_name': fileName,
            'file_extension': fileType,
            'link_url': arguments[2] || ''
        });
    };

    // Track outbound links
    SitecoreAnalytics.trackOutbound = function(url) {
        this.trackEvent('outbound_click', {
            'link_url': url,
            'link_domain': new URL(url).hostname
        });
    };

    // Track video interactions
    SitecoreAnalytics.trackVideo = function(action, videoName) {
        this.trackEvent('video_' + action, {
            'video_title': videoName,
            'video_url': arguments[2] || ''
        });
    };

    // Track search
    SitecoreAnalytics.trackSearch = function(searchTerm, resultCount) {
        this.trackEvent('search', {
            'search_term': searchTerm,
            'search_results': resultCount
        });
    };

})(window);

Include in layout:

@* /Views/Shared/_Layout.cshtml *@
<script src="/scripts/analytics/sitecore-events.js"></script>

3. Automatic Event Tracking

Automatically track all downloads and outbound links:

// /scripts/analytics/auto-tracking.js
document.addEventListener('DOMContentLoaded', function() {

    // Track all download links
    document.querySelectorAll('a[href]').forEach(function(link) {
        var href = link.getAttribute('href');

        // Check if it's a download (pdf, doc, zip, etc.)
        var downloadExtensions = /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar|txt|csv)$/i;
        if (downloadExtensions.test(href)) {
            link.addEventListener('click', function() {
                var fileName = href.split('/').pop();
                var extension = fileName.split('.').pop();

                SitecoreAnalytics.trackDownload(fileName, extension, href);
            });
        }

        // Check if it's an outbound link
        if (href.startsWith('http') && !href.includes(window.location.hostname)) {
            link.addEventListener('click', function() {
                SitecoreAnalytics.trackOutbound(href);
            });
        }
    });

    // Track mailto links
    document.querySelectorAll('a[href^="mailto:"]').forEach(function(link) {
        link.addEventListener('click', function() {
            gtag('event', 'email_click', {
                'email_address': link.getAttribute('href').replace('mailto:', '')
            });
        });
    });

    // Track tel links
    document.querySelectorAll('a[href^="tel:"]').forEach(function(link) {
        link.addEventListener('click', function() {
            gtag('event', 'phone_click', {
                'phone_number': link.getAttribute('href').replace('tel:', '')
            });
        });
    });
});

Sitecore Component Event Tracking

Track CTA Component Clicks

Controller:

// /Controllers/CTAController.cs
using System.Web.Mvc;
using Sitecore.Mvc.Controllers;

namespace YourProject.Controllers
{
    public class CTAController : SitecoreController
    {
        public ActionResult CTA()
        {
            return View();
        }
    }
}

View:

@* /Views/CTA/CTA.cshtml *@
@using Sitecore.Mvc

@{
    var buttonText = Html.Sitecore().Field("Button Text").ToString();
    var buttonLink = Html.Sitecore().Field("Button Link");
    var ctaName = Html.Sitecore().Field("CTA Name").ToString();
    var componentId = Model.RenderingItem.ID.ToString();
}

<div class="cta-component" data-component-id="@componentId">
    <a href="@buttonLink"
       class="cta-button"
       onclick="SitecoreAnalytics.trackCTA('@ctaName', '@Model.PageItem.Name');">
        @buttonText
    </a>
</div>

Track Hero Banner Interactions

@* /Views/Hero/HeroBanner.cshtml *@
@{
    var heroTitle = Html.Sitecore().Field("Title").ToString();
    var heroImage = Html.Sitecore().Field("Background Image");
}

<div class="hero-banner"
     data-tracking-component="hero_banner"
     data-tracking-title="@heroTitle">

    @Html.Sitecore().Field("Background Image")

    <div class="hero-content">
        @Html.Sitecore().Field("Title", new { @class = "hero-title" })
        @Html.Sitecore().Field("Subtitle")

        <button class="hero-cta"
                onclick="gtag('event', 'hero_cta_click', {
                    'hero_title': '@heroTitle',
                    'cta_text': this.innerText
                });">
            @Html.Sitecore().Field("CTA Text")
        </button>
    </div>
</div>

<script>
    // Track hero banner view
    gtag('event', 'hero_view', {
        'hero_title': '@heroTitle',
        'page_path': window.location.pathname
    });
</script>

Sitecore Forms Event Tracking

Track Form Submissions

Method 1: Submit Action (Recommended)

Create custom submit action:

// /Forms/SubmitActions/GoogleAnalyticsSubmitAction.cs
using System;
using Sitecore.Diagnostics;
using Sitecore.ExperienceForms.Models;
using Sitecore.ExperienceForms.Processing;
using Sitecore.ExperienceForms.Processing.Actions;

namespace YourProject.Forms.SubmitActions
{
    public class GoogleAnalyticsSubmitAction : SubmitActionBase<string>
    {
        public GoogleAnalyticsSubmitAction(ISubmitActionData submitActionData)
            : base(submitActionData)
        {
        }

        protected override bool Execute(string data, FormSubmitContext formSubmitContext)
        {
            Assert.ArgumentNotNull(formSubmitContext, nameof(formSubmitContext));

            var formName = formSubmitContext.FormName;
            var formId = formSubmitContext.FormId.ToString();

            // Track in Sitecore Analytics (xDB)
            if (Sitecore.Analytics.Tracker.Current != null &&
                Sitecore.Analytics.Tracker.Current.IsActive)
            {
                var currentPage = Sitecore.Analytics.Tracker.Current.Session.Interaction?.CurrentPage;
                if (currentPage != null)
                {
                    var pageEventId = Sitecore.Data.ID.Parse("{YOUR-FORM-SUBMIT-EVENT-ID}");
                    var pageEvent = currentPage.Register(pageEventId);
                    pageEvent.Data = formName;
                    pageEvent.Text = $"Form Submitted: {formName}";
                }
            }

            return true;
        }
    }
}

Register submit action:

<!-- App_Config/Include/Forms/CustomSubmitActions.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <submitActions>
      <submitAction name="Google Analytics Track"
                    type="YourProject.Forms.SubmitActions.GoogleAnalyticsSubmitAction, YourProject"/>
    </submitActions>
  </sitecore>
</configuration>

Method 2: Client-Side Form Tracking

// /scripts/analytics/form-tracking.js
document.addEventListener('DOMContentLoaded', function() {

    // Track Sitecore Forms submission
    var sitecoreForms = document.querySelectorAll('form[data-sc-fxb-form]');

    sitecoreForms.forEach(function(form) {
        var formName = form.getAttribute('data-sc-fxb-form') || 'Unknown Form';

        form.addEventListener('submit', function(e) {
            // Get form data
            var formData = new FormData(form);
            var formFields = {};

            for (var pair of formData.entries()) {
                if (pair[0] && pair[1]) {
                    formFields[pair[0]] = pair[1];
                }
            }

            // Track form submission
            gtag('event', 'form_submit', {
                'form_name': formName,
                'form_id': form.id,
                'form_location': window.location.pathname
            });

            // Track form start if not already tracked
            if (!sessionStorage.getItem('form_started_' + formName)) {
                gtag('event', 'form_start', {
                    'form_name': formName
                });
            }
        });

        // Track form start (first interaction)
        var formInputs = form.querySelectorAll('input, textarea, select');
        var formStarted = false;

        formInputs.forEach(function(input) {
            input.addEventListener('focus', function() {
                if (!formStarted) {
                    formStarted = true;
                    sessionStorage.setItem('form_started_' + formName, 'true');

                    gtag('event', 'form_start', {
                        'form_name': formName,
                        'form_id': form.id
                    });
                }
            }, { once: true });
        });
    });
});

Track Form Abandonment

// Track form abandonment
var formAbandonment = (function() {
    var formInteractions = {};

    function trackAbandonment(formName, formId) {
        gtag('event', 'form_abandonment', {
            'form_name': formName,
            'form_id': formId,
            'time_spent': formInteractions[formId] || 0
        });
    }

    // Monitor form interactions
    document.querySelectorAll('form[data-sc-fxb-form]').forEach(function(form) {
        var formId = form.id;
        var formName = form.getAttribute('data-sc-fxb-form');
        var startTime = null;

        form.addEventListener('focus', function() {
            if (!startTime) {
                startTime = Date.now();
            }
        }, true);

        // Track on page unload if form not submitted
        window.addEventListener('beforeunload', function() {
            if (startTime && !form.hasAttribute('data-submitted')) {
                formInteractions[formId] = Math.floor((Date.now() - startTime) / 1000);
                trackAbandonment(formName, formId);
            }
        });

        // Mark as submitted
        form.addEventListener('submit', function() {
            form.setAttribute('data-submitted', 'true');
        });
    });
})();

Integration with Sitecore xDB

Sync Events with xDB

Track GA4 events as Sitecore Goals:

// /Analytics/GoalHelper.cs
using Sitecore.Analytics;
using Sitecore.Data;

namespace YourProject.Analytics
{
    public static class GoalHelper
    {
        public static void TriggerGoal(ID goalId, string additionalData = null)
        {
            if (Tracker.Current != null && Tracker.Current.IsActive)
            {
                var currentPage = Tracker.Current.Session.Interaction?.CurrentPage;
                if (currentPage != null)
                {
                    var goal = currentPage.Register(goalId);
                    if (!string.IsNullOrEmpty(additionalData))
                    {
                        goal.Data = additionalData;
                    }
                }
            }
        }
    }
}

Use in Controller:

// Track both GA4 and Sitecore Goal
public ActionResult DownloadPDF(string fileName)
{
    // Trigger Sitecore Goal
    var downloadGoalId = ID.Parse("{YOUR-DOWNLOAD-GOAL-ID}");
    GoalHelper.TriggerGoal(downloadGoalId, fileName);

    // Return file
    return File(filePath, "application/pdf", fileName);
}

Corresponding client-side GA4 event:

gtag('event', 'file_download', {
    'file_name': fileName,
    'file_type': 'PDF'
});

Track Personalization with GA4

Track which personalization rules fire:

@using Sitecore.Analytics
@using Sitecore.Mvc.Analytics.Extensions

@{
    if (Tracker.Current != null && Tracker.Current.IsActive)
    {
        var currentPage = Tracker.Current.Session.Interaction?.CurrentPage;
        if (currentPage != null)
        {
            var personalizationData = Html.Sitecore().PersonalizationData();

            if (personalizationData != null)
            {
                <script>
                    gtag('event', 'personalization_applied', {
                        'rule_name': '@personalizationData.RuleName',
                        'component_name': '@Model.RenderingItem.Name'
                    });
                </script>
            }
        }
    }
}

Track Content Engagement

Scroll Depth Tracking

// /scripts/analytics/scroll-tracking.js
(function() {
    var scrollDepths = [25, 50, 75, 100];
    var tracked = {};

    function getScrollPercentage() {
        var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
        var scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
        return (scrollTop / scrollHeight) * 100;
    }

    function trackScrollDepth() {
        var scrollPercent = getScrollPercentage();

        scrollDepths.forEach(function(depth) {
            if (scrollPercent >= depth && !tracked[depth]) {
                tracked[depth] = true;

                gtag('event', 'scroll_depth', {
                    'percent_scrolled': depth,
                    'page_path': window.location.pathname,
                    'page_title': document.title
                });
            }
        });
    }

    // Throttle scroll events
    var scrollTimeout;
    window.addEventListener('scroll', function() {
        clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(trackScrollDepth, 100);
    }, { passive: true });
})();

Time on Page

// Track engaged time
var engagementTracker = (function() {
    var startTime = Date.now();
    var isEngaged = true;
    var totalEngagedTime = 0;
    var lastCheckTime = startTime;

    // Update engaged time
    function updateEngagedTime() {
        if (isEngaged) {
            var now = Date.now();
            totalEngagedTime += (now - lastCheckTime);
            lastCheckTime = now;
        }
    }

    // Track visibility changes
    document.addEventListener('visibilitychange', function() {
        if (document.hidden) {
            updateEngagedTime();
            isEngaged = false;
        } else {
            lastCheckTime = Date.now();
            isEngaged = true;
        }
    });

    // Track on page unload
    window.addEventListener('beforeunload', function() {
        updateEngagedTime();

        gtag('event', 'engaged_time', {
            'value': Math.floor(totalEngagedTime / 1000), // seconds
            'page_path': window.location.pathname
        });
    });
})();

Element Visibility Tracking

// Track when elements come into view
function trackElementVisibility(selector, eventName) {
    var elements = document.querySelectorAll(selector);

    var observer = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
            if (entry.isIntersecting && !entry.target.hasAttribute('data-tracked')) {
                entry.target.setAttribute('data-tracked', 'true');

                gtag('event', eventName, {
                    'element_id': entry.target.id,
                    'element_text': entry.target.innerText.substring(0, 100)
                });
            }
        });
    }, { threshold: 0.5 });

    elements.forEach(function(el) {
        observer.observe(el);
    });
}

// Track CTA visibility
trackElementVisibility('.cta-button', 'cta_view');

// Track product visibility
trackElementVisibility('.product-card', 'product_view');

SXA Component Tracking

Track SXA Component Views

@* Track SXA component rendering *@
@using Sitecore.XA.Foundation.Mvc.Extensions

@{
    var componentName = Model.RenderingItem.Name;
    var componentId = Model.RenderingItem.ID.ToString();
}

<div data-sxa-component="@componentName" data-component-id="@componentId">
    @Html.Sitecore().Placeholder("component-content")
</div>

<script>
    // Track component view
    gtag('event', 'component_view', {
        'component_name': '@componentName',
        'component_id': '@componentId',
        'page_path': window.location.pathname
    });
</script>
// Track SXA search results
document.addEventListener('DOMContentLoaded', function() {
    var searchForms = document.querySelectorAll('.search-box form');

    searchForms.forEach(function(form) {
        form.addEventListener('submit', function(e) {
            var searchInput = form.querySelector('input[type="text"]');
            var searchTerm = searchInput ? searchInput.value : '';

            if (searchTerm) {
                gtag('event', 'search', {
                    'search_term': searchTerm
                });

                // Track results after page loads
                setTimeout(function() {
                    var resultsCount = document.querySelectorAll('.search-result-item').length;

                    gtag('event', 'view_search_results', {
                        'search_term': searchTerm,
                        'results_count': resultsCount
                    });
                }, 1000);
            }
        });
    });
});

JSS/Headless Event Tracking

Next.js Event Tracking

// /src/lib/analytics.ts
export const trackEvent = (eventName: string, params?: Record<string, any>) => {
  if (typeof window !== 'undefined' && window.gtag) {
    window.gtag('event', eventName, params);
  }
};

export const trackCTA = (ctaName: string, ctaLocation: string) => {
  trackEvent('cta_click', {
    cta_name: ctaName,
    cta_location: ctaLocation,
  });
};

export const trackFormSubmit = (formName: string) => {
  trackEvent('form_submit', {
    form_name: formName,
  });
};

export const trackDownload = (fileName: string, fileType: string) => {
  trackEvent('file_download', {
    file_name: fileName,
    file_extension: fileType,
  });
};

Use in Component:

// /src/components/CTA/CTA.tsx
import { trackCTA } from '@/lib/analytics';
import { Text, Link } from '@sitecore-jss/sitecore-jss-nextjs';

const CTA = ({ fields }: CTAProps): JSX.Element => {
  const handleClick = () => {
    trackCTA(fields.ctaName.value, fields.ctaLocation.value);
  };

  return (
    <div className="cta-component">
      <Link field={fields.ctaLink} onClick={handleClick}>
        <Text field={fields.ctaText} />
      </Link>
    </div>
  );
};

export default CTA;

React JSS Hooks

// /src/hooks/useAnalytics.js
import { useEffect } from 'react';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-react';

export const usePageView = () => {
  const { sitecoreContext } = useSitecoreContext();

  useEffect(() => {
    if (!sitecoreContext.pageEditing && window.gtag) {
      window.gtag('event', 'page_view', {
        page_path: sitecoreContext.route?.itemPath,
        page_title: sitecoreContext.route?.name,
        page_template: sitecoreContext.route?.templateName,
      });
    }
  }, [sitecoreContext]);
};

export const useEventTracking = () => {
  return {
    trackEvent: (eventName, params) => {
      if (window.gtag) {
        window.gtag('event', eventName, params);
      }
    },
    trackCTA: (ctaName, ctaLocation) => {
      if (window.gtag) {
        window.gtag('event', 'cta_click', {
          cta_name: ctaName,
          cta_location: ctaLocation,
        });
      }
    },
  };
};

Use in Component:

import { useEventTracking } from '@/hooks/useAnalytics';

const CTAComponent = ({ fields }) => {
  const { trackCTA } = useEventTracking();

  const handleClick = () => {
    trackCTA(fields.ctaName.value, 'homepage-hero');
  };

  return (
    <button onClick={handleClick}>
      {fields.ctaText.value}
    </button>
  );
};

Custom Dimensions and Metrics

Configure Custom Dimensions

Set up in GA4:

  1. Go to Admin > Custom Definitions
  2. Create custom dimensions for Sitecore-specific data

Implement in Sitecore:

@{
    var itemTemplate = Sitecore.Context.Item.TemplateName;
    var itemPath = Sitecore.Context.Item.Paths.FullPath;
    var siteName = Sitecore.Context.Site.Name;
    var language = Sitecore.Context.Language.Name;
}

<script>
    gtag('config', 'G-XXXXXXXXXX', {
        'custom_map': {
            'dimension1': 'sitecore_template',
            'dimension2': 'sitecore_path',
            'dimension3': 'sitecore_site',
            'dimension4': 'sitecore_language'
        },
        'sitecore_template': '@itemTemplate',
        'sitecore_path': '@itemPath',
        'sitecore_site': '@siteName',
        'sitecore_language': '@language'
    });
</script>

Debugging Event Tracking

Debug Mode

// Enable GA4 debug mode
gtag('config', 'G-XXXXXXXXXX', {
    'debug_mode': true
});

// Log all events to console
var originalGtag = window.gtag;
window.gtag = function() {
    console.log('GA4 Event:', arguments);
    originalGtag.apply(this, arguments);
};

Test Events

Use GA4 DebugView:

  1. Enable debug mode (above)
  2. Visit your Sitecore site
  3. Trigger events
  4. Check GA4 > Admin > DebugView

Sitecore Analytics Debugging

// Log GA4 events to Sitecore log
using Sitecore.Diagnostics;

public static void LogAnalyticsEvent(string eventName, object eventData)
{
    Log.Info($"GA4 Event: {eventName} - Data: {Newtonsoft.Json.JsonConvert.SerializeObject(eventData)}", "Analytics");
}

Next Steps

// SYS.FOOTER