Google Tag Manager Setup on Kentico | OpsBlu Docs

Google Tag Manager Setup on Kentico

Complete guide to installing and configuring Google Tag Manager on Kentico Xperience websites

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

For MVC Development Model (Kentico 12+)

  1. Locate Your Layout File

Navigate to ~/Views/Shared/_Layout.cshtml or your custom layout file.

  1. 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>
  1. Make It Dynamic with Kentico Settings

Create Setting in Kentico:

  • Go to SettingsIntegrationGoogle
  • 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)

  1. Access Master Page

    • Go to DesignMaster pages
    • Edit your root master page
  2. 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>
  1. 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:

  1. Create Web Part

    • Go to DevelopmentWeb partsNew web part
    • Name: GoogleTagManager
    • Parent category: "Analytics"
  2. Web Part Properties

Add properties:

  • ContainerID (Text) - GTM Container ID
  • DataLayerName (Text) - Data layer variable name (default: "dataLayer")
  • LoadAsync (Boolean) - Load GTM asynchronously
  1. 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 -->
<% } %>
  1. 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:

  1. Create Site-Specific Settings

    • Go to Settings[Site Name]
    • Create GoogleTagManagerID at site level
    • Enter different GTM container IDs per site
  2. 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

  1. Create Tags in GTM

Navigate to your GTM container:

  1. 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
  1. 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

  1. Open GTM container
  2. Click Preview button
  3. Enter your Kentico site URL
  4. 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.js loads
  • 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

  1. Use Descriptive Naming: Name tags, triggers, and variables clearly
  2. Version Control: Use GTM versions and workspaces
  3. Test Before Publishing: Always use Preview mode
  4. Document Changes: Add notes when publishing versions
  5. Limit Container Users: Only grant access to necessary team members
  6. Use Folders: Organize tags, triggers, and variables in folders
  7. Create a Naming Convention: Consistent naming across all elements
  8. 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

Additional Resources