Install Google Tag Manager on Concrete CMS | OpsBlu Docs

Install Google Tag Manager on Concrete CMS

How to install GTM on Concrete CMS (formerly Concrete5) using Header/Footer blocks, page templates, or theme customization.

Google Tag Manager (GTM) is the recommended method for managing analytics and marketing tags on Concrete CMS. This guide covers installation methods, best practices, and Concrete CMS-specific considerations.

Why Use GTM on Concrete CMS?

Benefits:

  • Manage all tags from one interface (GA4, Meta Pixel, etc.)
  • No code changes required after initial installation
  • Better performance (single container vs multiple scripts)
  • Easier for marketers to update without developer help
  • Built-in debugging and preview tools
  • Version control and workspace management

Concrete CMS-Specific Advantages:

  • Works across all page types and templates
  • Easy integration with form submissions
  • Can exclude edit mode and dashboard pages
  • Compatible with marketplace add-ons
  • Survives theme updates when properly implemented

Installation Methods

Method Difficulty Flexibility Updates Recommended
Header/Footer Block Easy Low Dashboard Best for most sites
Page Template Medium Medium File editing Custom themes
Theme Customization Advanced High File editing Developers

The simplest method using Concrete CMS's built-in tracking code functionality.

Step 1: Create GTM Account and Container

  1. Go to Google Tag Manager

  2. Create Account

    • Account Name: Your company name
    • Country: Your location
  3. Create Container

    • Container Name: Your website domain (e.g., example.com)
    • Target Platform: Web
  4. Accept Terms of Service

  5. Copy Container Code

    You'll see two code snippets:

    • Head snippet: Goes in <head>
    • Body snippet: Goes in <body>

    Your Container ID will look like: GTM-XXXXXXX

Step 2: Add GTM via Dashboard

Concrete CMS v9+:

  1. Access Tracking Codes

    • Log in to Dashboard
    • Go to System & SettingsSEO & StatisticsTracking Codes
  2. Add GTM Head Code

    In the "Tracking Code (Header)" field, add:

    <!-- 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 -->
    

    Replace GTM-XXXXXXX with your actual Container ID.

  3. Add GTM Body Code

    If your version supports "Tracking Code (Body)" or "Additional Scripts", add:

    <!-- 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) -->
    

    Note: Some Concrete CMS versions only have header tracking codes. In that case, you'll need to add the noscript via page template (see Method 2).

  4. Save Settings

  5. Clear Cache

    • Go to DashboardSystem & SettingsOptimizationClear Cache
    • Clear cache to ensure code appears

Step 3: Verify Installation

  1. Use GTM Preview Mode

    • In GTM, click Preview button
    • Enter your website URL
    • Click Connect
    • Navigate your site and verify GTM Tag Assistant shows events
  2. Check Browser Console

    Open browser developer tools (F12) and run:

    console.log(window.dataLayer);
    

    You should see an array (may be empty initially, but should exist).

  3. Verify on Multiple Pages

    • Homepage
    • Blog posts
    • Form pages
    • Landing pages

Method 2: Page Template Installation

For more control or if built-in tracking codes aren't sufficient.

Step 1: Locate Theme Files

Your theme files are typically in:

/application/themes/[your-theme-name]/

Or for package themes:

/packages/[package-name]/themes/[theme-name]/

Step 2: Edit Page Template

Open your theme's main layout file (often view.php, default.php, or page_theme.php).

Add GTM Head Code:

Find the </head> closing tag and add above it:

<?php
// Exclude GTM from edit mode and dashboard
if (!$c->isEditMode() && !$this->controller->isControllerTaskInstanceOf('DashboardPageController')) {
    ?>
    <!-- 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 -->
    <?php
}
?>
</head>

Add GTM Body Code:

Find the opening <body> tag and add immediately after it:

<body>
<?php
if (!$c->isEditMode() && !$this->controller->isControllerTaskInstanceOf('DashboardPageController')) {
    ?>
    <!-- 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) -->
    <?php
}
?>

<!-- rest of body content -->

Replace GTM-XXXXXXX with your actual Container ID in both places.

Step 3: Clear Cache and Test

  • Dashboard → System & Settings → Optimization → Clear Cache
  • Visit your site and verify GTM loads

Method 3: Theme Customization (Advanced)

For developers who want maximum control.

Using Concrete CMS Page Event

Add GTM code via page event in your theme's page_theme.php:

<?php
namespace Application\Theme\YourTheme;

use Concrete\Core\Page\Theme\Theme;

class PageTheme extends Theme
{
    public function registerAssets()
    {
        // Register assets if needed
    }

    public function getThemeName()
    {
        return t('Your Theme Name');
    }

    public function getThemeDescription()
    {
        return t('Your theme description');
    }
}

Then in your view files, add conditional GTM loading.

Using Header/Footer Assets

In your theme's elements/header.php or elements/footer.php:

<?php
$c = Page::getCurrentPage();
$app = \Concrete\Core\Support\Facade\Application::getFacadeApplication();
$u = $app->make(\Concrete\Core\User\User::class);

// Only load on public pages
if (!$c->isEditMode() &&
    !$this->controller->isControllerTaskInstanceOf('DashboardPageController') &&
    !$u->isSuperUser()) {
    ?>
    <!-- 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 -->
    <?php
}
?>

Excluding Admin and Edit Pages

Always exclude these from GTM tracking:

<?php
$c = Page::getCurrentPage();

// Check if NOT in edit mode AND NOT in dashboard
if (!$c->isEditMode() &&
    !$this->controller->isControllerTaskInstanceOf('DashboardPageController')) {
    // Add GTM code here
}
?>

Additional exclusions (optional):

<?php
$app = \Concrete\Core\Support\Facade\Application::getFacadeApplication();
$c = Page::getCurrentPage();
$u = $app->make(\Concrete\Core\User\User::class);

// Exclude edit mode, dashboard, and super users
if (!$c->isEditMode() &&
    !$this->controller->isControllerTaskInstanceOf('DashboardPageController') &&
    !$u->isSuperUser()) {
    // Add GTM code
}
?>

Configure GTM for Concrete CMS

Once GTM is installed, configure it to work with Concrete CMS.

1. Enable Built-in Variables

In GTM:

  • Go to VariablesConfigure
  • Enable these built-in variables:
    • Page URL
    • Page Path
    • Page Hostname
    • Referrer
    • Click Element
    • Click Classes
    • Click ID
    • Click URL
    • Click Text
    • Form Element
    • Form Classes
    • Form ID

2. Create Concrete CMS Data Layer Variables

See Concrete CMS Data Layer Documentation for full variable setup.

Common variables to create:

Page Type:

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: pageType
  • Name: CMS - Page Type

Page ID:

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: pageID
  • Name: CMS - Page ID

User Type:

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: userType
  • Name: CMS - User Type

3. Create Concrete CMS Event Triggers

Form Submission:

  • Type: Form Submission
  • Name: Concrete CMS - Form Submit
  • Fires on: All Forms

File Download:

  • Type: Click - All Elements
  • Name: Concrete CMS - File Download
  • Fires on: Some Clicks
  • Condition: Click URL matches RegEx \.(pdf|doc|docx|xls|xlsx|zip)$

External Links:

  • Type: Click - All Elements
  • Name: Concrete CMS - External Link
  • Fires on: Some Clicks
  • Condition: Click URL does not contain \{\{Page Hostname\}\}

4. Publish Your Container

  • Click Submit in GTM
  • Name the version (e.g., "Initial Concrete CMS Setup")
  • Add description
  • Click Publish

Initialize Concrete CMS Data Layer

Add to your page template before GTM code:

<?php
if (!$c->isEditMode() && !$this->controller->isControllerTaskInstanceOf('DashboardPageController')) {
    $app = \Concrete\Core\Support\Facade\Application::getFacadeApplication();
    $u = $app->make(\Concrete\Core\User\User::class);

    $pageType = $c->getPageTypeHandle();
    $userType = $u->isRegistered() ? 'registered' : 'guest';
    ?>
    <script>
    window.dataLayer = window.dataLayer || [];
    dataLayer.push({
        'pageType': '<?php echo $pageType; ?>',
        'pageID': '<?php echo $c->getCollectionID(); ?>',
        'pageName': '<?php echo addslashes($c->getCollectionName()); ?>',
        'userType': '<?php echo $userType; ?>',
        'userID': '<?php echo $u->getUserID(); ?>'
    });
    </script>
    <?php
}
?>

Place this before the GTM container code.

Testing & Debugging

Use GTM Preview Mode

  1. In GTM, click Preview
  2. Enter your Concrete CMS site URL
  3. Click Connect
  4. GTM Tag Assistant opens in new window
  5. Navigate your site and watch events fire in real-time

Debug Checklist

  • GTM container loads on all public pages
  • Container does NOT load in edit mode
  • Container does NOT load on dashboard pages
  • Data layer variables populate correctly
  • Custom triggers fire correctly
  • Tags send data to analytics platforms
  • No JavaScript errors in console

Common Debug Commands

Check if GTM is loaded:

console.log(window.google_tag_manager);
// Should show object with your container ID

View data layer:

console.log(window.dataLayer);
// Should show array with pushed data

Monitor data layer pushes:

const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
  console.log('Data Layer Push:', arguments[0]);
  originalPush.apply(window.dataLayer, arguments);
};

Performance Optimization

1. Minimize Container Size

  • Remove unused tags, triggers, and variables
  • Avoid adding too many tags to single trigger
  • Use tag sequencing for dependent tags
  • Regularly audit and clean up

2. Use Async Loading

GTM loads asynchronously by default. Don't modify this unless absolutely necessary.

3. Monitor Impact

GTM can impact Concrete CMS performance if not optimized:

4. Consolidate Tags

Instead of installing multiple tracking scripts:

  • Install only GTM in theme
  • Add all tracking pixels through GTM
  • Remove marketplace tracking add-ons that duplicate functionality

Wait for user consent before firing marketing tags:

<script>
window.dataLayer = window.dataLayer || [];

// Check for consent cookie
const hasConsent = document.cookie.indexOf('tracking_consent=true') !== -1;

dataLayer.push({
  'event': 'consent_status',
  'analytics_consent': hasConsent ? 'granted' : 'denied',
  'marketing_consent': hasConsent ? 'granted' : 'denied'
});

// Listen for consent changes
document.addEventListener('consent_updated', function(e) {
  dataLayer.push({
    'event': 'consent_update',
    'analytics_consent': e.detail.analytics ? 'granted' : 'denied',
    'marketing_consent': e.detail.marketing ? 'granted' : 'denied'
  });
});
</script>

In GTM, create triggers based on these consent events to control when tags fire.

Troubleshooting

GTM Container Not Loading

Check:

  • Container ID is correct (GTM-XXXXXXX)
  • Script is in both <head> and <body>
  • No JavaScript errors blocking execution
  • Concrete CMS cache cleared
  • Not in edit mode or dashboard
  • Browser cache cleared

Data Layer Empty

Diagnosis:

console.log(window.dataLayer);
// Should return array, not undefined

If undefined:

  • GTM not properly installed
  • JavaScript errors blocking initialization
  • Cache not cleared

Events Not Firing

See Events Not Firing Troubleshooting for detailed debugging.

Container Loads in Edit Mode

Problem: GTM tracking when editing pages.

Solution: Add edit mode check:

<?php if (!$c->isEditMode()) { ?>
    <!-- GTM code -->
<?php } ?>

Changes Not Appearing

Problem: Updated GTM code doesn't show.

Solution: Clear Concrete CMS cache:

  • Dashboard → System & Settings → Optimization → Clear Cache
  • Or CLI: concrete/bin/concrete5 c5:clear-cache

Next Steps

For general GTM concepts, see Google Tag Manager Guide.