This guide covers how to install and configure Google Tag Manager (GTM) on Kentico Xperience websites. GTM provides a flexible way to manage all your marketing and analytics tags without requiring code deployments.
Why Use GTM with Kentico?
Benefits:
- Deploy tags without touching Kentico code
- Manage all marketing tags in one place
- Better performance with asynchronous tag loading
- Built-in tag templates for common tools
- Version control and rollback capabilities
- Collaboration features for marketing teams
- Preview and debug mode before publishing
Prerequisites
- Google Tag Manager account created
- GTM Container ID (format:
GTM-XXXXXXX) - Admin access to Kentico
- Access to edit layout files or master pages
Method 1: Direct Implementation (Recommended)
For MVC Development Model (Kentico 12+)
- Locate Your Layout File
Navigate to ~/Views/Shared/_Layout.cshtml or your custom layout file.
- Add GTM Container Code
Add this code immediately after the opening <head> tag:
<!DOCTYPE html>
<html>
<head>
<!-- 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 -->
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
@* Other head elements *@
</head>
<body>
<!-- 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) -->
@* Rest of body content *@
</body>
</html>
- Make It Dynamic with Kentico Settings
Create Setting in Kentico:
- Go to Settings → Integration → Google
- Create new setting key:
GoogleTagManagerID - Enter your GTM Container ID
Update Layout File:
@using CMS.Helpers
@using CMS.SiteProvider
@{
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
}
<!DOCTYPE html>
<html>
<head>
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- 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 -->
}
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
</head>
<body>
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@gtmId"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
}
@RenderBody()
</body>
</html>
For Portal Engine (Legacy)
Access Master Page
- Go to Design → Master pages
- Edit your root master page
Add GTM Code
In the Master page markup, add after opening <head> tag:
<head runat="server">
<!-- 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 -->
@* Other head content *@
</head>
Add after opening <body> tag:
<body>
<!-- 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) -->
@* Rest of body content *@
</body>
- Using Kentico Macros for Dynamic Values
{% raw %}{%
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID");
if (!String.IsNullOrEmpty(gtmId)) {
%}
<!-- 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 -->
{% } %}{% endraw %}
Method 2: Using Custom Web Part (Portal Engine)
Create a reusable web part for GTM:
Create Web Part
- Go to Development → Web parts → New web part
- Name:
GoogleTagManager - Parent category: "Analytics"
Web Part Properties
Add properties:
ContainerID(Text) - GTM Container IDDataLayerName(Text) - Data layer variable name (default: "dataLayer")LoadAsync(Boolean) - Load GTM asynchronously
- Web Part Code (ASCX)
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GoogleTagManager.ascx.cs" Inherits="CustomWebParts.GoogleTagManager" %>
<% if (!String.IsNullOrEmpty(ContainerID)) { %>
<!-- 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','<%= DataLayerName %>','<%= ContainerID %>');</script>
<!-- End Google Tag Manager -->
<% } %>
- Add to Master Page
- Drag Web Part to master page header
- Set ContainerID property
- Save and publish
Method 3: Custom Module (Enterprise/Advanced)
For centralized control across multiple sites:
using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.Helpers;
using CMS.SiteProvider;
[assembly: RegisterModule(typeof(GoogleTagManagerModule))]
public class GoogleTagManagerModule : Module
{
public GoogleTagManagerModule() : base("GoogleTagManagerIntegration") { }
protected override void OnInit()
{
base.OnInit();
// Register GTM script injection
RequestEvents.Begin.Execute += InjectGTMScript;
}
private void InjectGTMScript(object sender, EventArgs e)
{
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
if (string.IsNullOrEmpty(gtmId))
return;
// Head script
string gtmHeadScript = $@"
<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>";
CMS.Base.Web.UI.ScriptHelper.RegisterClientScriptBlock(
typeof(string),
"GTMHead",
gtmHeadScript,
false
);
// Body noscript
string gtmBodyScript = $@"
<noscript><iframe src='https://www.googletagmanager.com/ns.html?id={gtmId}'
height='0' width='0' style='display:none;visibility:hidden'></iframe></noscript>";
// Store for rendering in body
RequestStockHelper.Add("GTMBodyScript", gtmBodyScript);
}
}
Multi-Site Configuration
For Kentico installations with multiple sites:
Create Site-Specific Settings
- Go to Settings → [Site Name]
- Create
GoogleTagManagerIDat site level - Enter different GTM container IDs per site
Multi-Site Layout Code
@using CMS.Helpers
@using CMS.SiteProvider
@{
var currentSite = SiteContext.CurrentSite;
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", currentSite.SiteID);
}
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- GTM for @currentSite.SiteName -->
<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>
}
Environment-Based Loading
Prevent GTM from loading in non-production environments:
Using Environment Variables
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
var environment = Configuration["Environment"];
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
var shouldLoadGTM = environment == "Production" && !string.IsNullOrEmpty(gtmId);
}
@if (shouldLoadGTM)
{
<!-- GTM code -->
}
Using Kentico Site Name
@{
var isProduction = SiteContext.CurrentSite.SiteName == "ProductionSite";
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
}
@if (isProduction && !string.IsNullOrEmpty(gtmId))
{
<!-- GTM code -->
}
Using Custom Setting
Create a setting: EnableGoogleTagManager (Boolean)
@{
var gtmEnabled = SettingsKeyInfoProvider.GetBoolValue("EnableGoogleTagManager", SiteContext.CurrentSiteID);
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
}
@if (gtmEnabled && !string.IsNullOrEmpty(gtmId))
{
<!-- GTM code -->
}
GTM Container Setup
Basic Container Configuration
- Create Tags in GTM
Navigate to your GTM container:
- Tags → New → Choose tag type
- Common tags: GA4 Configuration, Google Ads, Meta Pixel
- Set Up Triggers
- Page View: All Pages
- Form Submission: Form ID or Class
- Click: Click URL, Click Classes, Click ID
- Custom Event: Based on data layer events
- Configure Variables
Built-in variables to enable:
- Page URL
- Page Path
- Page Hostname
- Referrer
- Click Element
- Click URL
- Form Element
GTM Workspace Setup for Kentico
Create custom variables for Kentico-specific data:
JavaScript Variable - Document Type:
function() {
return window.kenticoPageData?.documentType || 'unknown';
}
JavaScript Variable - Page Template:
function() {
return window.kenticoPageData?.pageTemplate || 'unknown';
}
JavaScript Variable - User Authenticated:
function() {
return window.kenticoPageData?.userAuthenticated || false;
}
Testing Your Implementation
1. GTM Preview Mode
- Open GTM container
- Click Preview button
- Enter your Kentico site URL
- GTM debugger will open showing:
- Tags fired
- Tags not fired
- Variables
- Data layer
2. Verify GTM Installation
Check browser console:
// Check if data layer exists
console.log(window.dataLayer);
// Check GTM is loaded
console.log(google_tag_manager);
3. Browser Developer Tools
- Open Network tab
- Filter by "gtm"
- Verify
gtm.jsloads - Check for 200 status code
4. Tag Assistant (Chrome Extension)
- Install Google Tag Assistant
- Navigate to your site
- Click extension icon
- Verify GTM container is present and working
Common GTM Tags for Kentico
GA4 Configuration Tag
Tag Type: Google Analytics: GA4 Configuration Measurement ID: G-XXXXXXXXXX Trigger: All Pages
GA4 Event Tag (Form Submission)
Tag Type: Google Analytics: GA4 Event Event Name: form_submit Event Parameters:
- form_name:
\{\{Form Name\}\} - page_location:
\{\{Page URL\}\}
Trigger: Form Submission - Contact Form
Meta Pixel Base Code
Tag Type: Custom HTML HTML:
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'YOUR_PIXEL_ID');
fbq('track', 'PageView');
</script>
Trigger: All Pages
Performance Optimization
1. Use GTM Server-Side Tagging
Benefits:
- Reduced client-side JavaScript
- Better page load performance
- Enhanced data security
- Ad blocker resistance
2. Optimize Tag Loading
- Use Custom HTML tags sparingly
- Prefer built-in tag templates
- Set appropriate tag firing priorities
- Use tag sequencing for dependencies
3. Limit Tag Execution
- Use specific triggers (not "All Pages" for everything)
- Implement tag firing limits
- Use tag scheduling for temporary campaigns
Troubleshooting
GTM Not Loading
Check:
- Container ID is correct (GTM-XXXXXXX format)
- Script is in
<head>section - No JavaScript errors blocking execution
- Ad blockers disabled for testing
Tags Not Firing
Debug:
- Use GTM Preview mode
- Check trigger conditions
- Verify variables are populated
- Review data layer structure
Data Layer Issues
Common Problems:
- Data layer pushed before GTM loads
- Incorrect data layer syntax
- Missing variable values
- Timing issues with AJAX content
Solution:
// Ensure GTM is loaded before pushing
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'custom_event',
'eventData': { /* data */ }
});
Best Practices
- Use Descriptive Naming: Name tags, triggers, and variables clearly
- Version Control: Use GTM versions and workspaces
- Test Before Publishing: Always use Preview mode
- Document Changes: Add notes when publishing versions
- Limit Container Users: Only grant access to necessary team members
- Use Folders: Organize tags, triggers, and variables in folders
- Create a Naming Convention: Consistent naming across all elements
- Monitor Tag Performance: Check page load impact regularly
Security Considerations
Content Security Policy (CSP)
If using CSP headers, whitelist GTM domains:
Content-Security-Policy:
script-src 'self' 'unsafe-inline' https://www.googletagmanager.com;
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com;
Prevent Unauthorized Changes
- Enable 2-factor authentication for GTM account
- Use container publishing workflow
- Limit user permissions appropriately
- Review container changes regularly
Next Steps
- Implement Data Layer for advanced tracking
- Set up GA4 in GTM
- Configure E-commerce Tracking
- Review GTM Best Practices