Installing Google Tag Manager on Sitecore
General GTM Concepts: See Google Tag Manager documentation for universal GTM concepts and tag configuration.
This guide covers Sitecore-specific methods for implementing Google Tag Manager (GTM), from traditional MVC implementations to headless JSS setups, with support for multi-site configurations and Experience Editor compatibility.
Prerequisites
Before installing GTM on Sitecore:
Create GTM Container
- Sign in to tagmanager.google.com
- Create a new container for your website
- Copy your Container ID (format:
GTM-XXXXXXX)
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
- Development environment (Visual Studio)
- Deploy permissions to Sitecore instance
Method 1: Layout File Integration (MVC)
Best for: Quick setup, standard Sitecore MVC sites
Basic Layout Implementation
Edit your main layout file:
@* /Views/Shared/_Layout.cshtml *@
<!DOCTYPE html>
<html lang="@Sitecore.Context.Language.Name">
<head>
<meta charset="utf-8" />
<title>@Html.Sitecore().Field("Title", new { DisableWebEdit = true })</title>
@if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<!-- 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-XXXXXXX');</script>
<!-- End Google Tag Manager -->
}
@Html.Sitecore().Placeholder("head")
</head>
<body>
@if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
}
@Html.Sitecore().Placeholder("main")
@RenderBody()
</body>
</html>
Multi-Site Configuration
1. Add site configuration:
<!-- 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"
gtmContainerId="GTM-XXXXXXX1" />
<site name="website2"
hostName="www.site2.com"
database="web"
gtmContainerId="GTM-XXXXXXX2" />
</sites>
</sitecore>
</configuration>
2. Access in Layout:
@{
var gtmId = Sitecore.Context.Site.Properties["gtmContainerId"];
}
@if (!string.IsNullOrEmpty(gtmId) && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<!-- 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','@gtmId');</script>
<!-- End Google Tag Manager -->
}
Method 2: View Rendering Component
Best for: Reusable component, managed through Sitecore Content Editor
Step 1: Create Data Template
Template Structure:
/sitecore/templates/Feature/Analytics/GTM Settings
├── Fields
│ ├── Container ID (Single-Line Text)
│ ├── Enable GTM (Checkbox)
│ ├── Data Layer Name (Single-Line Text) - Default: "dataLayer"
│ └── Environment Variables (Multi-Line Text)
Step 2: Create Settings Item
Content Editor:
- Navigate to
/sitecore/content/[Your Site]/Settings - Insert new item from GTM Settings template
- Fill in Container ID:
GTM-XXXXXXX - Check Enable GTM
- Data Layer Name:
dataLayer
Step 3: Create View
@* /Views/Analytics/GoogleTagManager.cshtml *@
@using Sitecore.Data.Items
@{
var settingsPath = $"/sitecore/content/{Sitecore.Context.Site.Name}/Settings/GTM Settings";
var settingsItem = Sitecore.Context.Database.GetItem(settingsPath);
if (settingsItem != null && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
var containerId = settingsItem["Container ID"];
var enableGTM = settingsItem["Enable GTM"] == "1";
var dataLayerName = settingsItem["Data Layer Name"];
if (string.IsNullOrEmpty(dataLayerName))
{
dataLayerName = "dataLayer";
}
if (enableGTM && !string.IsNullOrEmpty(containerId))
{
@* Head Section *@
<text>
<!-- 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!='@dataLayerName'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','@dataLayerName','@containerId');</script>
<!-- End Google Tag Manager -->
</text>
}
}
}
Step 4: Create Separate Noscript Component
@* /Views/Analytics/GoogleTagManagerNoScript.cshtml *@
@using Sitecore.Data.Items
@{
var settingsPath = $"/sitecore/content/{Sitecore.Context.Site.Name}/Settings/GTM Settings";
var settingsItem = Sitecore.Context.Database.GetItem(settingsPath);
if (settingsItem != null && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
var containerId = settingsItem["Container ID"];
var enableGTM = settingsItem["Enable GTM"] == "1";
if (enableGTM && !string.IsNullOrEmpty(containerId))
{
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@containerId"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
}
}
}
Step 5: Add Renderings to Presentation
Add both renderings to your layout's presentation details:
- GTM Head in
<head>placeholder - GTM NoScript at start of
<body>
Method 3: Controller Rendering with Model
Best for: Complex logic, environment-specific configs
Create Model
// /Models/Analytics/GTMModel.cs
namespace YourProject.Models.Analytics
{
public class GTMModel
{
public string ContainerId { get; set; }
public bool Enabled { get; set; }
public string DataLayerName { get; set; } = "dataLayer";
public string EnvironmentAuth { get; set; }
public string EnvironmentPreview { get; set; }
public bool IsExperienceEditor { get; set; }
public bool ShouldRender =>
Enabled &&
!string.IsNullOrEmpty(ContainerId) &&
!IsExperienceEditor;
public string GetGTMUrl()
{
var url = $"https://www.googletagmanager.com/gtm.js?id={ContainerId}";
if (!string.IsNullOrEmpty(EnvironmentAuth) && !string.IsNullOrEmpty(EnvironmentPreview))
{
url += $">m_auth={EnvironmentAuth}>m_preview={EnvironmentPreview}>m_cookies_win=x";
}
return url;
}
}
}
Create Controller
// /Controllers/AnalyticsController.cs
using System.Web.Mvc;
using Sitecore.Data.Items;
using YourProject.Models.Analytics;
namespace YourProject.Controllers
{
public class AnalyticsController : Controller
{
public ActionResult GoogleTagManager()
{
var model = new GTMModel
{
IsExperienceEditor = Sitecore.Context.PageMode.IsExperienceEditorEditing
};
var settingsPath = $"/sitecore/content/{Sitecore.Context.Site.Name}/Settings/GTM Settings";
var settingsItem = Sitecore.Context.Database.GetItem(settingsPath);
if (settingsItem != null)
{
model.ContainerId = settingsItem["Container ID"];
model.Enabled = settingsItem["Enable GTM"] == "1";
model.DataLayerName = settingsItem["Data Layer Name"];
model.EnvironmentAuth = settingsItem["Environment Auth"];
model.EnvironmentPreview = settingsItem["Environment Preview"];
if (string.IsNullOrEmpty(model.DataLayerName))
{
model.DataLayerName = "dataLayer";
}
}
return View("/Views/Analytics/GoogleTagManager.cshtml", model);
}
}
}
Create View
@* /Views/Analytics/GoogleTagManager.cshtml *@
@model YourProject.Models.Analytics.GTMModel
@if (Model.ShouldRender)
{
<!-- 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!='@Model.DataLayerName'?'&l='+l:'';j.async=true;j.src=
'@Html.Raw(Model.GetGTMUrl())';f.parentNode.insertBefore(j,f);
})(window,document,'script','@Model.DataLayerName','@Model.ContainerId');</script>
<!-- End Google Tag Manager -->
}
Method 4: Pipeline Processor (Site-Wide)
Best for: Enterprise implementations, centralized control
Create Pipeline Processor
// /Pipelines/HttpRequest/InjectGoogleTagManager.cs
using Sitecore.Pipelines.HttpRequest;
using System.Web;
namespace YourProject.Pipelines.HttpRequest
{
public class InjectGoogleTagManager : 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 container ID from site properties
var containerId = Sitecore.Context.Site.Properties["gtmContainerId"];
if (string.IsNullOrEmpty(containerId))
return;
// Store for rendering in layout
args.Context.Items["GTMContainerId"] = containerId;
args.Context.Items["GTMEnabled"] = true;
}
}
}
Register Pipeline
<!-- App_Config/Include/Analytics/GTM.config -->
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="YourProject.Pipelines.HttpRequest.InjectGoogleTagManager, YourProject"
patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
Access in Layout
@{
var gtmEnabled = HttpContext.Current.Items["GTMEnabled"] as bool?;
var gtmId = HttpContext.Current.Items["GTMContainerId"] as string;
}
@if (gtmEnabled == true && !string.IsNullOrEmpty(gtmId))
{
<!-- GTM code using @gtmId -->
}
Method 5: SXA (Sitecore Experience Accelerator)
Best for: SXA-based sites
Extend SXA Site Settings
1. Add fields to SXA Site Settings:
# Extend template: /sitecore/templates/Foundation/Experience Accelerator/Sites/Site
Fields:
- ID: "gtm-container-id"
Name: "GTM Container ID"
Type: "Single-Line Text"
Section: "Analytics"
- ID: "gtm-enabled"
Name: "GTM Enabled"
Type: "Checkbox"
Section: "Analytics"
2. Create SXA Component:
Create a new SXA component or rendering variant:
@* SXA GTM Component *@
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions
@{
var siteSettings = Html.Sxa().SiteSettings;
var gtmId = siteSettings?.GetItem()["GTM Container ID"];
var gtmEnabled = siteSettings?.GetItem()["GTM Enabled"] == "1";
}
@if (gtmEnabled && !string.IsNullOrEmpty(gtmId) && !Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
<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','@gtmId');</script>
}
3. Add to SXA Partial Design:
Add the GTM component to your site's partial design for automatic inclusion on all pages.
Method 6: Headless/JSS Implementation
Best for: Sitecore JSS (React, Vue, Angular, Next.js)
Next.js Implementation
1. Create GTM Component:
// /src/components/Analytics/GoogleTagManager.tsx
import Script from 'next/script';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
interface GTMProps {
containerId: string;
dataLayerName?: string;
}
const GoogleTagManager = ({ containerId, dataLayerName = 'dataLayer' }: GTMProps): JSX.Element | null => {
const { sitecoreContext } = useSitecoreContext();
// Don't render in Experience Editor
if (sitecoreContext?.pageEditing) {
return null;
}
if (!containerId) {
return null;
}
return (
<>
{/* GTM Script */}
<Script
id="google-tag-manager"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(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!='${dataLayerName}'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','${dataLayerName}','${containerId}');
`,
}}
/>
{/* GTM NoScript */}
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${containerId}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
</>
);
};
export default GoogleTagManager;
2. Add to Layout:
// /src/Layout.tsx
import GoogleTagManager from './components/Analytics/GoogleTagManager';
const Layout = ({ layoutData }: LayoutProps): JSX.Element => {
const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID || '';
return (
<>
<GoogleTagManager containerId={GTM_ID} />
<div>
{/* Your layout content */}
</div>
</>
);
};
3. Environment Configuration:
# .env.local
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX
# .env.production
NEXT_PUBLIC_GTM_ID=GTM-PRODXXX
React JSS Implementation
// /src/components/Analytics/GoogleTagManager.js
import { useEffect } from 'react';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-react';
const GoogleTagManager = ({ fields }) => {
const { sitecoreContext } = useSitecoreContext();
const containerId = fields?.containerId?.value || process.env.REACT_APP_GTM_ID;
const dataLayerName = fields?.dataLayerName?.value || 'dataLayer';
useEffect(() => {
// Don't load in Experience Editor
if (sitecoreContext.pageEditing || !containerId) {
return;
}
// Inject GTM 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!=dataLayerName?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script',dataLayerName,containerId);
}, [containerId, dataLayerName, sitecoreContext.pageEditing]);
// Render noscript
if (sitecoreContext.pageEditing || !containerId) {
return null;
}
return (
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${containerId}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
);
};
export default GoogleTagManager;
Sitecore Personalization Integration
Tracking Personalization Rules
Capture which personalization rules are active on the current page:
@using Sitecore.Analytics
@using Sitecore.Analytics.Model
@using Sitecore.Mvc.Analytics.Extensions
@{
var personalizedComponents = new List<object>();
var isPersonalized = Html.IsPersonalized();
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var interaction = Tracker.Current.Interaction;
// Track personalization data
}
}
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'sitecore_personalization',
'personalization': {
'is_personalized': @isPersonalized.ToString().ToLower(),
'components': @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(personalizedComponents))
}
});
</script>
A/B Testing Integration
Track Sitecore content testing (A/B tests):
@using Sitecore.Analytics.Testing
@using Sitecore.ContentTesting
@{
var testingData = new Dictionary<string, object>();
// Check if current page is part of a test
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var currentItem = Sitecore.Context.Item;
var testingProvider = TestingProvider.Current;
if (testingProvider != null)
{
var activeTests = testingProvider.GetActiveTests(currentItem);
foreach (var test in activeTests)
{
testingData.Add(test.TestDefinitionId.ToString(), new
{
testId = test.TestDefinitionId.ToString(),
testName = test.TestDefinitionName,
variationId = test.VariationId?.ToString(),
variationName = test.VariationId != null ? currentItem.Database.GetItem(test.VariationId)?.Name : "Original"
});
}
}
}
}
<script>
@if (testingData.Any())
{
<text>
dataLayer.push({
'event': 'sitecore_ab_test',
'experiment': {
'id': '@testingData.First().Value.testId',
'name': '@testingData.First().Value.testName',
'variant': '@testingData.First().Value.variationName'
}
});
</text>
}
</script>
Profile Card Tracking
Track visitor profile cards and pattern matches:
@using Sitecore.Analytics
@using Sitecore.Analytics.Tracking
@{
var profileData = new Dictionary<string, object>();
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var contact = Tracker.Current.Contact;
if (contact != null)
{
// Get behavior profile data
var behaviorProfiles = contact.GetBehaviorProfiles();
foreach (var profile in behaviorProfiles.Profiles)
{
profileData[profile.ProfileName] = new
{
profileName = profile.ProfileName,
patternMatch = profile.PatternLabel,
totalScore = profile.Total,
values = profile.Values.Select(v => new { key = v.Key, value = v.Value })
};
}
}
}
}
<script>
@if (profileData.Any())
{
<text>
dataLayer.push({
'event': 'sitecore_profile_data',
'visitor_profile': @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(profileData))
});
</text>
}
</script>
Goal Tracking
Automatically push Sitecore goals to GTM:
@using Sitecore.Analytics
@using Sitecore.Analytics.Model
@{
var triggeredGoals = new List<object>();
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var currentPage = Tracker.Current.Interaction.CurrentPage;
if (currentPage != null)
{
var goals = currentPage.PageEvents.Where(e => e.IsGoal);
foreach (var goal in goals)
{
triggeredGoals.Add(new
{
goalId = goal.PageEventDefinitionId,
goalName = goal.Name,
goalValue = goal.Value,
timestamp = goal.DateTime
});
}
}
}
}
<script>
@foreach (var goal in triggeredGoals)
{
<text>
dataLayer.push({
'event': 'sitecore_goal',
'goal': {
'id': '@goal.goalId',
'name': '@goal.goalName',
'value': @goal.goalValue
}
});
</text>
}
</script>
Campaign Tracking
Track Sitecore campaign information:
@using Sitecore.Analytics
@{
var campaignInfo = new Dictionary<string, string>();
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var interaction = Tracker.Current.Interaction;
var campaignId = interaction?.CampaignId;
if (campaignId != null && campaignId != Guid.Empty)
{
var campaign = Sitecore.Context.Database.GetItem(new Sitecore.Data.ID(campaignId));
if (campaign != null)
{
campaignInfo["id"] = campaignId.ToString();
campaignInfo["name"] = campaign.Name;
campaignInfo["group"] = campaign.Parent?.Name ?? "";
}
}
}
}
<script>
@if (campaignInfo.Any())
{
<text>
dataLayer.push({
'event': 'sitecore_campaign',
'campaign': {
'id': '@campaignInfo["id"]',
'name': '@campaignInfo["name"]',
'group': '@campaignInfo["group"]'
}
});
</text>
}
</script>
Data Layer Setup for Sitecore Content
Page Context Data Layer
Push comprehensive Sitecore page context to GTM:
@using Sitecore.Data.Fields
@using Sitecore.Data.Items
@{
var currentItem = Sitecore.Context.Item;
var site = Sitecore.Context.Site;
}
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'page_data',
'page': {
// Item information
'item_id': '@currentItem.ID',
'item_name': '@currentItem.Name',
'item_path': '@currentItem.Paths.FullPath',
'display_name': '@currentItem.DisplayName',
// Template information
'template_id': '@currentItem.TemplateID',
'template_name': '@currentItem.TemplateName',
// Version and language
'language': '@Sitecore.Context.Language.Name',
'version': '@currentItem.Version.Number',
// Site context
'site_name': '@site.Name',
'hostname': '@site.HostName',
'database': '@Sitecore.Context.Database.Name',
// Page metadata
'page_title': '@Html.Raw(currentItem["Title"] ?? currentItem.DisplayName)',
'page_type': '@currentItem.TemplateName',
// Publishing info
'created_date': '@currentItem.Statistics.Created.ToString("yyyy-MM-dd")',
'updated_date': '@currentItem.Statistics.Updated.ToString("yyyy-MM-dd")',
'created_by': '@currentItem.Statistics.CreatedBy',
'updated_by': '@currentItem.Statistics.UpdatedBy'
}
});
</script>
User Engagement Data Layer
Track user interaction depth:
@using Sitecore.Analytics
@{
var engagementValue = 0;
var visitNumber = 0;
var pageViews = 0;
if (Tracker.Current != null && Tracker.Current.IsActive)
{
var interaction = Tracker.Current.Interaction;
var contact = Tracker.Current.Contact;
if (interaction != null)
{
engagementValue = interaction.Value;
pageViews = interaction.GetPages().Count();
}
if (contact != null)
{
visitNumber = contact.System.VisitCount;
}
}
}
<script>
dataLayer.push({
'event': 'user_engagement',
'engagement': {
'value': @engagementValue,
'visit_number': @visitNumber,
'page_views_session': @pageViews,
'is_returning_visitor': @(visitNumber > 1).ToString().ToLower()
}
});
</script>
Multi-Language Site Data Layer
For multi-language Sitecore sites:
@{
var currentLanguage = Sitecore.Context.Language;
var availableLanguages = Sitecore.Context.Database.GetItem(Sitecore.Context.Item.ID)
?.Languages
.Select(l => l.Name)
.ToList() ?? new List<string>();
}
<script>
dataLayer.push({
'event': 'language_context',
'language': {
'current': '@currentLanguage.Name',
'iso_code': '@currentLanguage.CultureInfo.TwoLetterISOLanguageName',
'culture': '@currentLanguage.CultureInfo.Name',
'display_name': '@currentLanguage.CultureInfo.DisplayName',
'available_languages': @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(availableLanguages))
}
});
</script>
Sitecore Commerce Data Layer
For Sitecore Commerce implementations:
@using Sitecore.Commerce.Entities.Carts
@using Sitecore.Commerce.Services.Carts
@{
// Get current cart
var cartService = new CartServiceProvider();
var cart = cartService.GetCurrentCart();
}
<script>
@if (cart != null && cart.Lines.Any())
{
<text>
dataLayer.push({
'event': 'ecommerce_context',
'ecommerce': {
'currency': '@cart.CurrencyCode',
'value': @cart.Lines.Sum(l => l.Total.Amount),
'items': [
@foreach (var line in cart.Lines)
{
<text>{
'item_id': '@line.Product.ProductId',
'item_name': '@line.Product.DisplayName',
'item_category': '@line.Product.ProductCatalog',
'price': @line.Product.ListPrice,
'quantity': @line.Quantity
},</text>
}
]
}
});
</text>
}
</script>
GTM Environment Configuration
For staging and production environments:
1. Configure environments in GTM:
- Create environment in GTM
- Get auth and preview tokens
2. Use in Sitecore:
@{
var gtmId = "GTM-XXXXXXX";
var gtmAuth = Sitecore.Configuration.Settings.GetSetting("GTM.Auth");
var gtmPreview = Sitecore.Configuration.Settings.GetSetting("GTM.Preview");
var gtmUrl = $"https://www.googletagmanager.com/gtm.js?id={gtmId}";
if (!string.IsNullOrEmpty(gtmAuth) && !string.IsNullOrEmpty(gtmPreview))
{
gtmUrl += $">m_auth={gtmAuth}>m_preview={gtmPreview}>m_cookies_win=x";
}
}
<script>
// Use @gtmUrl instead of standard URL
</script>
3. Configure in web.config:
<!-- Web.Release.config -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="GTM.ContainerId" value="GTM-PRODXXX" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="GTM.Auth" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="GTM.Preview" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
<!-- Web.Staging.config -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="GTM.ContainerId" value="GTM-STGXXX" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="GTM.Auth" value="your-auth-token" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="GTM.Preview" value="env-123" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Server-Side GTM (sGTM)
For server-side tagging:
@{
var serverContainerUrl = "https://your-gtm-server.com";
var containerId = "GTM-XXXXXXX";
}
<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=
'@serverContainerUrl/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','@containerId');</script>
Cookie Consent Integration
Implement consent before loading GTM:
<script>
// Check consent before loading GTM
function loadGTM() {
if (localStorage.getItem('cookieConsent') === 'true') {
(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-XXXXXXX');
}
}
// Load immediately if consent given
if (localStorage.getItem('cookieConsent') === 'true') {
loadGTM();
}
// Listen for consent events
document.addEventListener('cookieConsentGranted', loadGTM);
</script>
Validation and Testing
1. GTM Preview Mode
- In GTM, click Preview
- Enter your Sitecore site URL
- Navigate site to test tags
- Verify tags fire correctly
2. Check Experience Editor
Verify GTM doesn't load in Experience Editor:
- Open page in Experience Editor
- Check browser console - GTM should not load
- Verify no errors
3. Browser DevTools
// Check if dataLayer exists (browser console)
console.log(window.dataLayer);
// Monitor dataLayer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('dataLayer.push:', arguments);
return originalPush.apply(this, arguments);
};
4. Google Tag Assistant
Use Tag Assistant:
- Connect to your Sitecore site
- Verify GTM container loads
- Check for configuration errors
Common Sitecore-Specific Issues
Issue: GTM Loads in Experience Editor
Cause: Missing Experience Editor check
Solution:
@if (!Sitecore.Context.PageMode.IsExperienceEditorEditing)
{
@* GTM code *@
}
Issue: Multi-Site Wrong Container
Cause: Shared GTM configuration
Solution: Use site-specific settings (see Multi-Site Configuration above)
Issue: dataLayer Not Defined
Cause: GTM script loaded too late
Solution: Ensure GTM is in <head> before other scripts that use dataLayer
Issue: Noscript Tag Not Rendering
Cause: Missing body rendering
Solution: Place noscript immediately after opening <body> tag
Performance Optimization
Preconnect to GTM Domains
<link rel="preconnect" href="https://www.googletagmanager.com">
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
Delayed Loading
// Load GTM after user interaction
let gtmLoaded = false;
const loadGTM = () => {
if (gtmLoaded) return;
gtmLoaded = true;
(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-XXXXXXX');
};
['mousedown', 'touchstart', 'scroll', 'keydown'].forEach(event => {
window.addEventListener(event, loadGTM, {once: true, passive: true});
});
// Fallback after 3 seconds
setTimeout(loadGTM, 3000);
Next Steps
- Configure GTM Data Layer for Sitecore-specific variables
- Set Up GA4 via GTM
- Debug Tracking Issues - Sitecore-specific tracking troubleshooting
- Events Not Firing (Global) - Universal tracking debugging guide