Installing Google Analytics 4 on Sitecore
General GA4 Concepts: See Google Analytics for universal GA4 concepts and features.
This guide covers Sitecore-specific methods for implementing Google Analytics 4 (GA4), from traditional MVC to headless JSS implementations, with support for Sitecore XP, XM, and SXA.
Prerequisites
Before installing GA4 on Sitecore:
Create a GA4 Property in Google Analytics
- Sign in to analytics.google.com
- Create a new GA4 property
- Copy your Measurement ID (format:
G-XXXXXXXXXX)
Verify Sitecore Version
- Sitecore XP/XM 10.x (recommended)
- Sitecore 9.x (supported)
- Sitecore JSS 21.x+ (for headless)
Backend Access Requirements
- Content Editor access
- Visual Studio / development environment
- Deploy permissions to Sitecore instance
Development Environment
- .NET Framework 4.8+ or .NET Core 3.1+
- Visual Studio 2019/2022
- Sitecore TDS, Unicorn, or Sitecore CLI for deployments
Method 1: Layout File Integration (MVC)
Best for: Quick setup, standard Sitecore MVC implementations
Basic Layout Implementation
Edit your main layout file (typically /Views/Shared/_Layout.cshtml):
@using Sitecore.Mvc
@using Sitecore.Mvc.Analytics.Extensions
<!DOCTYPE html>
<html lang="@Sitecore.Context.Language.Name">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@Html.Sitecore().Field("Title", new { DisableWebEdit = true })</title>
@Html.Sitecore().Placeholder("head")
@* Only load analytics in normal/preview mode, not Experience Editor *@
@if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
});
</script>
}
</head>
<body>
@Html.Sitecore().Placeholder("main")
@RenderBody()
</body>
</html>
Multi-Site Configuration
For Sitecore multi-site setups, configure per-site tracking IDs:
1. Add to site definition in web.config or patch file:
<!-- App_Config/Include/Sites/Sites.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<sites>
<site name="website1"
hostName="www.site1.com"
database="web"
googleAnalyticsId="G-XXXXXXXXX1" />
<site name="website2"
hostName="www.site2.com"
database="web"
googleAnalyticsId="G-XXXXXXXXX2" />
</sites>
</sitecore>
</configuration>
2. Access in Layout:
@{
var gaId = Sitecore.Context.Site.Properties["googleAnalyticsId"];
}
@if (!string.IsNullOrEmpty(gaId) && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<script async src="https://www.googletagmanager.com/gtag/js?id=@gaId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@gaId', {
'anonymize_ip': true
});
</script>
}
Configuration-Based Approach
1. Add to appSettings in Web.config:
<configuration>
<appSettings>
<add key="GoogleAnalytics.MeasurementId" value="G-XXXXXXXXXX" />
<add key="GoogleAnalytics.Enabled" value="true" />
</appSettings>
</configuration>
2. Create helper class:
// /Models/Analytics/GoogleAnalyticsConfig.cs
namespace YourProject.Models.Analytics
{
public static class GoogleAnalyticsConfig
{
public static string MeasurementId =>
System.Configuration.ConfigurationManager.AppSettings["GoogleAnalytics.MeasurementId"];
public static bool Enabled =>
bool.Parse(System.Configuration.ConfigurationManager.AppSettings["GoogleAnalytics.Enabled"] ?? "false");
public static bool ShouldRender =>
Enabled &&
!Sitecore.Context.PageMode.IsExperienceEditorEditing &&
!string.IsNullOrEmpty(MeasurementId);
}
}
3. Use in Layout:
@using YourProject.Models.Analytics
@if (GoogleAnalyticsConfig.ShouldRender)
{
<script async src="https://www.googletagmanager.com/gtag/js?id=@GoogleAnalyticsConfig.MeasurementId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@GoogleAnalyticsConfig.MeasurementId', {
'anonymize_ip': true
});
</script>
}
Method 2: View Rendering Component
Best for: Reusable component, managed through Sitecore Content Editor
Step 1: Create Rendering Item
Content Editor:
- Navigate to
/sitecore/layout/Renderings/Feature/Analytics - Insert new View Rendering
- Name: "Google Analytics"
- Path:
/Views/Analytics/GoogleAnalytics.cshtml
Step 2: Create Data Template
Template Definition:
/sitecore/templates/Feature/Analytics/Google Analytics Settings
├── Fields
│ ├── Measurement ID (Single-Line Text)
│ ├── Enable Tracking (Checkbox)
│ ├── Anonymize IP (Checkbox)
│ └── Enhanced Measurement (Checkbox)
Template in TDS or Unicorn:
# /serialization/Templates/Analytics/GoogleAnalyticsSettings.yml
---
ID: "{12345678-1234-1234-1234-123456789012}"
Path: /sitecore/templates/Feature/Analytics/Google Analytics Settings
Template: /sitecore/templates/System/Templates/Template
Fields:
- ID: "measurement-id"
Name: "Measurement ID"
Type: "Single-Line Text"
- ID: "enable-tracking"
Name: "Enable Tracking"
Type: "Checkbox"
Value: "1"
- ID: "anonymize-ip"
Name: "Anonymize IP"
Type: "Checkbox"
Value: "1"
Step 3: Create Settings Item
Content Editor:
- Navigate to
/sitecore/content/[Your Site]/Settings - Insert new item from Google Analytics Settings template
- Fill in Measurement ID and options
Step 4: Create View
@* /Views/Analytics/GoogleAnalytics.cshtml *@
@using Sitecore.Mvc
@using Sitecore.Data.Items
@{
// Get settings from Sitecore
var settingsPath = "/sitecore/content/" + Sitecore.Context.Site.Name + "/Settings/Google Analytics Settings";
var settingsItem = Sitecore.Context.Database.GetItem(settingsPath);
if (settingsItem != null)
{
var measurementId = settingsItem["Measurement ID"];
var enableTracking = settingsItem["Enable Tracking"] == "1";
var anonymizeIp = settingsItem["Anonymize IP"] == "1";
if (enableTracking && !string.IsNullOrEmpty(measurementId) && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=@measurementId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@measurementId', {
@if (anonymizeIp)
{
<text>'anonymize_ip': true,</text>
}
'cookie_flags': 'SameSite=None;Secure'
});
</script>
}
}
}
Step 5: Add to Layout
Add the rendering to your page's presentation details or layout definition.
Method 3: Controller Rendering with Model
Best for: Complex logic, integration with Sitecore xDB
Step 1: Create Model
// /Models/Analytics/GoogleAnalyticsModel.cs
using Sitecore.Data.Items;
namespace YourProject.Models.Analytics
{
public class GoogleAnalyticsModel
{
public string MeasurementId { get; set; }
public bool EnableTracking { get; set; }
public bool AnonymizeIp { get; set; }
public bool EnhancedMeasurement { get; set; }
public string UserId { get; set; } // Sitecore Contact ID
public string Language { get; set; }
public string PageType { get; set; }
public bool ShouldRender =>
EnableTracking &&
!string.IsNullOrEmpty(MeasurementId) &&
!Sitecore.Context.PageMode.IsExperienceEditorEditing;
}
}
Step 2: Create Controller
// /Controllers/AnalyticsController.cs
using System.Web.Mvc;
using Sitecore.Analytics;
using Sitecore.Data.Items;
using YourProject.Models.Analytics;
namespace YourProject.Controllers
{
public class AnalyticsController : Controller
{
public ActionResult GoogleAnalytics()
{
var model = new GoogleAnalyticsModel();
// Get settings from Sitecore
var settingsPath = $"/sitecore/content/{Sitecore.Context.Site.Name}/Settings/Google Analytics Settings";
var settingsItem = Sitecore.Context.Database.GetItem(settingsPath);
if (settingsItem != null)
{
model.MeasurementId = settingsItem["Measurement ID"];
model.EnableTracking = settingsItem["Enable Tracking"] == "1";
model.AnonymizeIp = settingsItem["Anonymize IP"] == "1";
model.EnhancedMeasurement = settingsItem["Enhanced Measurement"] == "1";
}
// Get Sitecore context data
model.Language = Sitecore.Context.Language.Name;
model.PageType = Sitecore.Context.Item?.TemplateName;
// Get Sitecore Contact ID (if xDB is enabled)
if (Tracker.Current != null && Tracker.Current.Contact != null)
{
model.UserId = Tracker.Current.Contact.ContactId.ToString();
}
return View("/Views/Analytics/GoogleAnalytics.cshtml", model);
}
}
}
Step 3: Create Strongly-Typed View
@* /Views/Analytics/GoogleAnalytics.cshtml *@
@model YourProject.Models.Analytics.GoogleAnalyticsModel
@if (Model.ShouldRender)
{
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=@Model.MeasurementId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@Model.MeasurementId', {
@if (Model.AnonymizeIp)
{
<text>'anonymize_ip': true,</text>
}
@if (!string.IsNullOrEmpty(Model.UserId))
{
<text>'user_id': '@Model.UserId',</text>
}
'language': '@Model.Language',
'page_type': '@Model.PageType',
'cookie_flags': 'SameSite=None;Secure'
});
</script>
}
Step 4: Register Controller Rendering
Content Editor:
- Navigate to
/sitecore/layout/Renderings/Feature/Analytics - Insert new Controller Rendering
- Controller:
YourProject.Controllers.AnalyticsController, YourProject - Controller Action:
GoogleAnalytics
Method 4: Pipeline Processor (Site-Wide)
Best for: Enterprise implementations, site-wide tracking
Step 1: Create Pipeline Processor
// /Pipelines/HttpRequest/AddGoogleAnalytics.cs
using System.Web;
using Sitecore.Pipelines.HttpRequest;
namespace YourProject.Pipelines.HttpRequest
{
public class AddGoogleAnalytics : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
// Skip if in Experience Editor
if (Sitecore.Context.PageMode.IsExperienceEditorEditing)
return;
// Skip if no context item
if (Sitecore.Context.Item == null)
return;
// Get measurement ID from site properties
var measurementId = Sitecore.Context.Site.Properties["googleAnalyticsId"];
if (string.IsNullOrEmpty(measurementId))
return;
// Add GA4 script to page header
var script = $@"
<script async src='https://www.googletagmanager.com/gtag/js?id={measurementId}'></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){{dataLayer.push(arguments);}}
gtag('js', new Date());
gtag('config', '{measurementId}', {{
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
}});
</script>";
// Inject into response (requires additional configuration)
// This is a simplified example
args.Context.Items["GoogleAnalytics"] = script;
}
}
}
Step 2: Register Pipeline
<!-- App_Config/Include/Analytics/GoogleAnalytics.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="YourProject.Pipelines.HttpRequest.AddGoogleAnalytics, YourProject"
patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
Method 5: SXA (Sitecore Experience Accelerator)
Best for: SXA-based sites
Create SXA Component
1. Create Rendering Variant:
Navigate to your SXA site's rendering variants and create:
/sitecore/content/[Your Site]/Presentation/Rendering Variants/Analytics
├── Google Analytics
│ ├── Script Tag (Variant Field: Script)
2. Configure Variant Script:
<script async src="https://www.googletagmanager.com/gtag/js?id=$googleAnalyticsId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '$googleAnalyticsId', {
'anonymize_ip': true
});
</script>
3. Add to Partial Design:
Add the Google Analytics component to your SXA partial design for site-wide inclusion.
SXA Site Settings Extension
1. Extend SXA Site Settings template:
# Add GA fields to Site Settings
Template: /sitecore/templates/Foundation/Experience Accelerator/Sites/Site
Fields:
- ID: "google-analytics-id"
Name: "Google Analytics ID"
Type: "Single-Line Text"
Section: "Analytics"
2. Access in SXA Component:
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions
@{
var siteSettings = Html.Sxa().SiteSettings;
var gaId = siteSettings?.GetItem()["Google Analytics ID"];
}
@if (!string.IsNullOrEmpty(gaId))
{
<script>
gtag('config', '@gaId');
</script>
}
Method 6: Headless/JSS Implementation
Best for: Sitecore JSS (React, Vue, Angular, Next.js)
Next.js Implementation
1. Create Analytics Component:
// /src/components/Analytics/GoogleAnalytics.tsx
import Script from 'next/script';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
interface GoogleAnalyticsProps {
measurementId: string;
}
const GoogleAnalytics = ({ measurementId }: GoogleAnalyticsProps): JSX.Element | null => {
const { sitecoreContext } = useSitecoreContext();
// Don't render in Experience Editor
if (sitecoreContext?.pageEditing) {
return null;
}
if (!measurementId) {
return null;
}
return (
<>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${measurementId}', {
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
});
`}
</Script>
</>
);
};
export default GoogleAnalytics;
2. Add to Layout:
// /src/Layout.tsx
import GoogleAnalytics from './components/Analytics/GoogleAnalytics';
const Layout = ({ layoutData }: LayoutProps): JSX.Element => {
const GA_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || '';
return (
<>
<GoogleAnalytics measurementId={GA_ID} />
<div>
{/* Your layout content */}
</div>
</>
);
};
3. Environment Configuration:
# .env.local
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
React JSS Implementation
// /src/components/Analytics/GoogleAnalytics.js
import React, { useEffect } from 'react';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-react';
const GoogleAnalytics = ({ fields }) => {
const { sitecoreContext } = useSitecoreContext();
const measurementId = fields?.measurementId?.value || process.env.REACT_APP_GA_ID;
useEffect(() => {
// Don't load in Experience Editor
if (sitecoreContext.pageEditing || !measurementId) {
return;
}
// Load GA4
const script = document.createElement('script');
script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`;
script.async = true;
document.head.appendChild(script);
// Initialize GA4
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', measurementId, {
'anonymize_ip': true,
'language': sitecoreContext.language,
'page_path': sitecoreContext.route?.itemPath
});
}, [measurementId, sitecoreContext]);
return null; // No UI rendering
};
export default GoogleAnalytics;
JSS Component Definition
// /sitecore/definitions/components/GoogleAnalytics.sitecore.js
export default function GoogleAnalytics(manifest) {
manifest.addComponent({
name: 'GoogleAnalytics',
displayName: 'Google Analytics',
fields: [
{ name: 'measurementId', type: manifest.fieldTypes.singleLineText },
{ name: 'anonymizeIp', type: manifest.fieldTypes.checkbox },
],
});
}
Integration with Sitecore xDB
Sync GA4 with Sitecore Analytics:
@using Sitecore.Analytics
@{
string userId = null;
string sessionId = null;
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var contact = Tracker.Current.Contact;
if (contact != null)
{
userId = contact.ContactId.ToString("N");
}
var session = Tracker.Current.Session;
if (session != null)
{
sessionId = session.SessionId.ToString("N");
}
}
}
<script>
gtag('config', 'G-XXXXXXXXXX', {
@if (!string.IsNullOrEmpty(userId))
{
<text>'user_id': '@userId',</text>
}
'custom_map': {
'dimension1': 'sitecore_contact_id',
'dimension2': 'sitecore_session_id'
},
'sitecore_contact_id': '@userId',
'sitecore_session_id': '@sessionId'
});
</script>
Data Layer Configuration for Sitecore XP/XM
Basic Data Layer Structure
Configure a Sitecore-specific data layer to capture CMS metadata:
@using Sitecore.Analytics
@using Sitecore.Data.Fields
@using Sitecore.Data.Items
@{
var currentItem = Sitecore.Context.Item;
var isPersonalized = Sitecore.Mvc.Analytics.Extensions.HtmlHelperExtensions.IsPersonalized(Html);
}
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'page_view',
'page_data': {
'item_id': '@currentItem.ID',
'item_name': '@currentItem.Name',
'template_id': '@currentItem.TemplateID',
'template_name': '@currentItem.TemplateName',
'item_path': '@currentItem.Paths.FullPath',
'language': '@Sitecore.Context.Language.Name',
'version': '@currentItem.Version.Number',
'is_personalized': @isPersonalized.ToString().ToLower(),
'site_name': '@Sitecore.Context.Site.Name',
'database': '@Sitecore.Context.Database.Name'
}
});
</script>
Sitecore XP Personalization Data
Track which personalization rules are active:
@using Sitecore.Analytics.Model
@using Sitecore.Marketing.Definitions
@using Sitecore.Marketing.Definitions.Goals
@{
var personalizations = new List<string>();
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var interaction = Tracker.Current.Interaction;
if (interaction != null)
{
// Track active goals
var goals = interaction.GetPages()
.SelectMany(p => p.PageEvents)
.Where(e => e.IsGoal)
.Select(e => e.Name);
// Track campaigns
var campaignId = Tracker.Current.Interaction.CampaignId;
}
}
}
<script>
dataLayer.push({
'event': 'personalization_data',
'personalization': {
'is_personalized': @isPersonalized.ToString().ToLower(),
'active_tests': [], // Add A/B test data if applicable
'profile_values': {}, // Add profile card values if needed
'campaign_id': '@campaignId'
}
});
</script>
Content Metadata Tracking
Capture content author, creation date, and workflow state:
@{
var currentItem = Sitecore.Context.Item;
// Get content metadata
var createdBy = currentItem.Statistics.CreatedBy;
var createdDate = currentItem.Statistics.Created;
var updatedBy = currentItem.Statistics.UpdatedBy;
var updatedDate = currentItem.Statistics.Updated;
// Get workflow state (if applicable)
var workflowState = string.Empty;
var workflowField = currentItem.Fields[Sitecore.FieldIDs.WorkflowState];
if (workflowField != null && !string.IsNullOrEmpty(workflowField.Value))
{
var stateItem = Sitecore.Context.Database.GetItem(workflowField.Value);
workflowState = stateItem?.Name ?? string.Empty;
}
}
<script>
dataLayer.push({
'event': 'content_metadata',
'content': {
'created_by': '@createdBy',
'created_date': '@createdDate.ToString("yyyy-MM-dd")',
'updated_by': '@updatedBy',
'updated_date': '@updatedDate.ToString("yyyy-MM-dd")',
'workflow_state': '@workflowState',
'item_version': '@currentItem.Version.Number'
}
});
</script>
Sitecore Commerce Integration
For Sitecore Commerce implementations:
@using Sitecore.Commerce.Entities.Carts
@using Sitecore.Commerce.Services.Carts
@{
// Get current cart (simplified example)
var cartService = new CartServiceProvider();
var cart = cartService.GetCurrentCart();
if (cart != null && cart.Lines.Any())
{
var cartValue = cart.Lines.Sum(l => l.Total.Amount);
var itemCount = cart.Lines.Sum(l => l.Quantity);
}
}
<script>
@if (cart != null)
{
<text>
dataLayer.push({
'event': 'cart_data',
'ecommerce': {
'currency': '@cart.CurrencyCode',
'value': @cartValue,
'items': [
@foreach (var line in cart.Lines)
{
<text>{
'item_id': '@line.Product.ProductId',
'item_name': '@line.Product.DisplayName',
'quantity': @line.Quantity,
'price': @line.Product.ListPrice
},</text>
}
]
}
});
</text>
}
</script>
Multi-Site Data Layer
For multi-site Sitecore installations:
@{
var site = Sitecore.Context.Site;
var siteSettings = site.SiteInfo;
}
<script>
dataLayer.push({
'event': 'site_context',
'site': {
'name': '@site.Name',
'hostname': '@site.HostName',
'virtual_folder': '@site.VirtualFolder',
'root_path': '@site.RootPath',
'start_item': '@site.StartPath',
'language': '@site.Language',
'database': '@site.Database.Name'
}
});
</script>
Language and Localization
Track language and country for multi-language sites:
@{
var language = Sitecore.Context.Language.Name;
var country = Sitecore.Context.Language.CultureInfo.TwoLetterISOLanguageName;
var displayName = Sitecore.Context.Language.CultureInfo.DisplayName;
}
<script>
gtag('config', 'G-XXXXXXXXXX', {
'language': '@language',
'content_group': '@country',
'custom_map': {
'dimension3': 'sitecore_language'
},
'sitecore_language': '@displayName'
});
</script>
Consent Mode v2
Implement Google Consent Mode for GDPR compliance:
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Default consent state (denied)
gtag('consent', 'default', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'wait_for_update': 500
});
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
// After user consent
document.addEventListener('cookieConsentGranted', function() {
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
});
</script>
Environment-Specific Configuration
Use web.config transformations for different environments:
Web.Release.config:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="GoogleAnalytics.MeasurementId"
value="G-PRODUCTION-ID"
xdt:Transform="SetAttributes"
xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Web.Debug.config:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="GoogleAnalytics.MeasurementId"
value="G-DEVELOPMENT-ID"
xdt:Transform="SetAttributes"
xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Caching Considerations
Sitecore's output caching:
Cache-Safe Implementation:
@* This is cached with the page *@
@Html.Sitecore().Placeholder("main")
@* This runs client-side, safe with caching *@
<script>
// Client-side dynamic data
gtag('config', 'G-XXXXXXXXXX', {
'page_path': window.location.pathname,
'page_title': document.title
});
</script>
Disable Caching for Dynamic Tracking:
<!-- In rendering definition -->
<rendering cacheable="false">
<!-- Your GA rendering -->
</rendering>
Validation and Testing
1. Experience Editor Test
Verify tracking doesn't load in Experience Editor:
- Open a page in Experience Editor
- Check browser console - GA should not load
- Verify no errors in console
2. Preview Mode Test
Test in Preview mode:
- Use Review > Preview in Sitecore
- Open browser DevTools > Network tab
- Look for requests to
googletagmanager.com/gtag/js - Check Real-Time reports in GA4
3. Published Site Test
Test on published site:
- Clear Sitecore HTML cache
- Visit published site as anonymous user
- Verify in Google Analytics Real-Time reports
4. Multi-Site Testing
For multi-site:
- Test each site domain
- Verify correct tracking ID loads per site
- Check Real-Time reports for each property
5. Sitecore-Specific Debugging Tools
Sitecore Debugger:
@* Add to layout for debugging *@
@if (Sitecore.Context.Diagnostics.Debugging)
{
<div style="position: fixed; bottom: 0; right: 0; background: #000; color: #fff; padding: 10px; z-index: 9999;">
<strong>Sitecore Debug Info:</strong><br/>
Item: @Sitecore.Context.Item?.Name<br/>
Template: @Sitecore.Context.Item?.TemplateName<br/>
Database: @Sitecore.Context.Database?.Name<br/>
Language: @Sitecore.Context.Language?.Name<br/>
Site: @Sitecore.Context.Site?.Name<br/>
Page Mode: @(Sitecore.Context.PageMode.IsExperienceEditorEditing ? "Experience Editor" : "Normal")<br/>
xDB Tracking: @(Sitecore.Analytics.Tracker.Current?.IsActive.ToString() ?? "N/A")
</div>
}
Enable Sitecore Debugging: In web.config:
<setting name="Sitecore.Diagnostics.EnableDebugging" value="true"/>
Check Sitecore Cache Stats:
Navigate to /sitecore/admin/cache.aspx to verify:
- HTML cache is enabled and working
- View the size of cached renderings
- Verify your GA rendering is being cached (if cacheable)
Sitecore Analytics Debugger:
@using Sitecore.Analytics
@if (Sitecore.Context.Diagnostics.Debugging && Tracker.Current != null)
{
<div style="background: #ffc; padding: 10px; border: 1px solid #cc0;">
<strong>xDB Debug Info:</strong><br/>
Contact ID: @Tracker.Current.Contact?.ContactId<br/>
Session ID: @Tracker.Current.Session?.SessionId<br/>
Is Active: @Tracker.Current.IsActive<br/>
Campaign ID: @Tracker.Current.Interaction?.CampaignId<br/>
Goals Triggered: @Tracker.Current.Interaction?.GetPages().SelectMany(p => p.PageEvents).Count(e => e.IsGoal)
</div>
}
6. Debugging Tools
Chrome DevTools:
// Check dataLayer (browser console)
console.log(window.dataLayer);
// Check gtag function
console.log(typeof gtag);
// Monitor all dataLayer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('dataLayer.push:', arguments);
return originalPush.apply(this, arguments);
};
// Check if Sitecore Contact ID is being sent
console.log(window.dataLayer.find(item => item.sitecore_contact_id));
Google Tag Assistant:
- Install Tag Assistant browser extension
- Connect to your site
- Verify GA4 tag fires correctly
- Check for Sitecore-specific custom dimensions
GA4 DebugView: Enable debug mode:
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true // Only enable for testing
});
</script>
Then check Admin > DebugView in GA4 to see real-time events with detailed parameters.
Common Sitecore-Specific Issues
Issue: Tracking Loads in Experience Editor
Cause: Missing Experience Editor check
Solution:
@if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
@* GA code *@
}
Issue: Different Tracking IDs Needed Per Site
Cause: Multi-site setup
Solution: Use site properties or settings items per site (see Method 1 above)
Issue: Tracking Not Working After Publish
Cause: HTML cache
Solution:
# Clear HTML cache
# In Sitecore: Control Panel > Database > Clean up databases > HTML cache
Or programmatically:
Sitecore.Caching.CacheManager.ClearAllCaches();
Issue: Session/User ID Not Syncing with xDB
Cause: Tracker not initialized
Solution:
// Ensure xDB is tracking
if (Tracker.Current != null && !Tracker.Current.IsActive)
{
Tracker.Current.StartTracking();
}
Performance Optimization
Preconnect to Google Domains
<link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
Delayed Loading
// Load GA4 after user interaction
let gaLoaded = false;
const loadGA = () => {
if (gaLoaded) return;
gaLoaded = true;
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
};
['mousedown', 'touchstart', 'scroll'].forEach(event => {
window.addEventListener(event, loadGA, {once: true, passive: true});
});
// Fallback after 5s
setTimeout(loadGA, 5000);
Next Steps
- Configure GA4 Event Tracking for Sitecore-specific events
- Set Up E-commerce Tracking for Sitecore Commerce
- Debug Tracking Issues - Sitecore-specific tracking troubleshooting
- Events Not Firing (Global) - Universal tracking debugging guide
Related Resources
- Google Analytics Platform Overview - Universal GA4 concepts
- Sitecore Documentation
- Sitecore MVC Documentation
- Sitecore JSS Documentation
- Google Tag Manager Setup - Alternative installation method