Drupal GTM Integration | Blue Frog Docs

Drupal GTM Integration

Integrate Google Tag Manager with Drupal for centralized tag management.

Drupal GTM Integration

Complete guide to setting up Google Tag Manager (GTM) on your Drupal site for centralized tracking and tag management.

Getting Started

GTM Setup Guide

Step-by-step instructions for installing GTM on Drupal.

Data Layer Implementation

Implement a comprehensive data layer for enhanced tracking capabilities.

Why Use GTM with Drupal?

GTM provides powerful tag management benefits:

  • Centralized Management: Control all tracking from one interface
  • No Code Deploys: Add/modify tags without site changes
  • Version Control: Track changes and roll back if needed
  • Preview Mode: Test tags before publishing
  • Advanced Triggers: Fire tags based on complex conditions

Prerequisites

Before installing GTM on Drupal:

  • Drupal 9 or Drupal 10 installation (or Drupal 7 for legacy sites)
  • Administrator access to Drupal admin panel
  • Google Tag Manager account created
  • GTM Container ID (format: GTM-XXXXXXX)
  • Composer access for module installation (recommended)
  • Understanding of Drupal's module system

Installation Methods

The official Drupal GTM module provides the most robust implementation:

Installation via Composer:

composer require drupal/google_tag
drush en google_tag

Configuration Steps:

  1. Navigate to Configuration > System > Google Tag Manager
  2. Click Add Container
  3. Configure container settings:
    • Label: Production Container
    • Container ID: GTM-XXXXXXX
    • Insert snippet on: All pages
    • Weight: -100 (load early)
  4. Configure insertion conditions:
    • Path patterns: Leave empty for all pages
    • Role conditions: Configure user role visibility if needed
  5. Click Save

Module Features:

  • Multiple container support
  • Role-based insertion
  • Path-based conditions
  • Data layer integration
  • Automatic noscript tag insertion
  • Cache management

Method 2: Theme Template Integration

For custom theme implementations:

  1. Edit your theme's html.html.twig template
  2. Add GTM head code after opening <head> tag:
{% if google_tag_manager_id %}
<!-- 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','{{ google_tag_manager_id }}');</script>
<!-- End Google Tag Manager -->
{% endif %}
  1. Add noscript code after opening <body> tag:
{% if google_tag_manager_id %}
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ google_tag_manager_id }}"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
{% endif %}
  1. Set container ID in theme settings or configuration

Method 3: Custom Module Development

For advanced control, create a custom module:

yourmodule.info.yml:

name: Custom GTM
type: module
description: 'Custom Google Tag Manager implementation'
core_version_requirement: ^9 || ^10
dependencies:
  - drupal:node

yourmodule.module:

<?php

/**
 * Implements hook_page_attachments().
 */
function yourmodule_page_attachments(array &$attachments) {
  $config = \Drupal::config('yourmodule.settings');
  $container_id = $config->get('gtm_container_id');

  if (!empty($container_id)) {
    // Add GTM script to head
    $attachments['#attached']['html_head'][] = [
      [
        '#type' => 'html_tag',
        '#tag' => 'script',
        '#value' => "(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','{$container_id}');",
      ],
      'google_tag_manager',
    ];
  }
}

Container ID Configuration

Managing Container IDs

The Google Tag Manager module supports multiple containers:

  1. Navigate to Configuration > System > Google Tag Manager
  2. Click Add Container for each environment
  3. Configure container conditions:
    • Production: Default container for all users
    • Development: Conditional on specific domain or IP
    • Staging: Path-based or role-based insertion

Environment-Specific Containers

// In settings.php
if (getenv('ENVIRONMENT') === 'production') {
  $config['google_tag.container.default']['container_id'] = 'GTM-PROD123';
} else {
  $config['google_tag.container.default']['container_id'] = 'GTM-DEV456';
}

Data Layer Implementation

Drupal Data Layer Module

Install the Data Layer module for structured data:

composer require drupal/datalayer
drush en datalayer

Configuration:

  1. Navigate to Configuration > Search and metadata > Data Layer
  2. Enable desired data layer variables:
    • Entity type (node, user, taxonomy term)
    • Entity bundle (article, page, product)
    • User information
    • Language
    • Page title
  3. Configure output method (globally or per entity)

Basic Data Layer Structure

<script>
dataLayer = [{
  'drupalLanguage': 'en',
  'drupalCountry': 'US',
  'entityType': 'node',
  'entityBundle': 'article',
  'entityId': '123',
  'entityTitle': 'Article Title',
  'entityCreated': '2024-01-15',
  'userStatus': 'authenticated',
  'userUid': '456'
}];
</script>

Content Type Specific Data Layer

For article nodes:

/**
 * Implements hook_page_attachments().
 */
function mymodule_page_attachments(array &$page) {
  $node = \Drupal::routeMatch()->getParameter('node');

  if ($node && $node->bundle() === 'article') {
    $data = [
      'contentType' => 'article',
      'contentId' => $node->id(),
      'contentTitle' => $node->getTitle(),
      'contentAuthor' => $node->getOwner()->getDisplayName(),
      'contentCategory' => $node->get('field_category')->entity->getName(),
      'contentTags' => array_map(function($term) {
        return $term->entity->getName();
      }, $node->get('field_tags')->getValue()),
    ];

    $page['#attached']['html_head'][] = [
      [
        '#type' => 'html_tag',
        '#tag' => 'script',
        '#value' => 'dataLayer.push(' . json_encode($data) . ');',
      ],
      'datalayer_article',
    ];
  }
}

Commerce Data Layer (Drupal Commerce)

For ecommerce tracking with Drupal Commerce:

/**
 * Product view data layer.
 */
function mymodule_commerce_product_view(array &$page) {
  $product = \Drupal::routeMatch()->getParameter('commerce_product');

  if ($product) {
    $variation = $product->getDefaultVariation();
    $data = [
      'event' => 'productDetail',
      'ecommerce' => [
        'detail' => [
          'products' => [[
            'name' => $product->getTitle(),
            'id' => $product->id(),
            'price' => $variation->getPrice()->getNumber(),
            'category' => $product->get('field_category')->entity->getName(),
          ]],
        ],
      ],
    ];

    $page['#attached']['drupalSettings']['gtm']['productData'] = $data;
  }
}

Form Submission Tracking

// Add to custom JavaScript file
(function ($, Drupal, drupalSettings) {
  'use strict';

  Drupal.behaviors.gtmFormTracking = {
    attach: function (context, settings) {
      $('form', context).once('gtm-form').on('submit', function() {
        dataLayer.push({
          'event': 'formSubmission',
          'formId': $(this).attr('id'),
          'formAction': $(this).attr('action')
        });
      });
    }
  };
})(jQuery, Drupal, drupalSettings);

Common Triggers and Tags

Essential Triggers for Drupal

Create these triggers in GTM:

  1. All Pages Trigger

    • Type: Page View
    • Fires on: All Pages
  2. Content View Trigger

    • Type: Custom Event
    • Event name: contentView
    • Condition: Entity Type equals node
  3. Form Submission Trigger

    • Type: Custom Event
    • Event name: formSubmission
  4. User Registration Trigger

    • Type: Custom Event
    • Event name: userRegistration
  5. Commerce Purchase Trigger (if using Drupal Commerce)

    • Type: Custom Event
    • Event name: purchase

Essential Tags

  1. Google Analytics 4 Configuration

    • Tag type: GA4 Configuration
    • Measurement ID: G-XXXXXXXXXX
    • Trigger: All Pages
  2. Content View Event

    • Tag type: GA4 Event
    • Event name: view_item
    • Parameters: Entity type, bundle, ID
    • Trigger: Content View
  3. Form Submission Event

    • Tag type: GA4 Event
    • Event name: form_submit
    • Parameters: Form ID, form action
    • Trigger: Form Submission

Variables Configuration

Create these variables for Drupal-specific data:

  1. Data Layer Variables:

    • DL - Entity Type
    • DL - Entity Bundle
    • DL - Entity ID
    • DL - User Status
    • DL - Language Code
  2. Custom JavaScript Variables:

    function() {
      return drupalSettings.path.currentPath;
    }
    
  3. DOM Variables:

    • Page Title: {{Page Title}}
    • Content Author: CSS Selector for author field

Preview and Debug Mode

Using GTM Preview with Drupal

  1. In GTM, click Preview button
  2. Enter your Drupal site URL
  3. Click Connect
  4. Navigate through different content types
  5. Verify data layer variables populate correctly

Debugging Data Layer

Use browser console to inspect data layer:

// View entire data layer
console.log(dataLayer);

// Watch for new pushes
var originalPush = dataLayer.push;
dataLayer.push = function() {
  console.log('Data Layer Push:', arguments);
  return originalPush.apply(this, arguments);
};

Drupal Debugging Tools

Enable development mode for debugging:

// In settings.local.php
$config['google_tag.container.default']['debug_output'] = TRUE;

Common Debug Checks

  • GTM container loads on all page types
  • Data layer includes entity information
  • User authentication status tracked correctly
  • Form IDs captured accurately
  • Commerce product data structured properly
  • No PHP errors in Drupal logs
  • Cache cleared after configuration changes

Publishing Workflow

Pre-Publishing Checklist

  • Test GTM on all content types (article, page, etc.)
  • Verify data layer on authenticated and anonymous users
  • Test form submissions
  • Check commerce events (if applicable)
  • Test on mobile and desktop
  • Clear Drupal cache
  • Review GTM preview summary
  • Validate no JavaScript console errors

Publishing Steps

  1. In GTM, click Submit
  2. Name version: "Drupal Production v1.0"
  3. Describe changes in detail
  4. Click Publish
  5. Monitor Drupal logs for issues
  6. Clear Drupal cache after publishing

Cache Management

Clear Drupal cache after GTM changes:

drush cache-rebuild

Or via admin UI: Configuration > Development > Performance > Clear all caches

Troubleshooting Common Issues

GTM Container Not Loading

Symptoms: Container code missing from page source

Solutions:

  • Verify Google Tag module is enabled: drush pm:list --status=enabled | grep google_tag
  • Check container configuration at /admin/config/system/google_tag
  • Clear Drupal cache: drush cr
  • Verify module weight (should load early)
  • Check path conditions aren't blocking insertion
  • Review role-based visibility settings

Data Layer Variables Empty

Symptoms: Data layer shows but variables are undefined

Solutions:

  • Verify Data Layer module is installed and enabled
  • Check entity has required fields populated
  • Clear render cache: drush cache:rebuild-all
  • Verify field machine names match data layer configuration
  • Check entity is loaded in current route context
  • Review PHP error logs: drush watchdog:show

Module Conflicts

Symptoms: GTM not working with other modules

Solutions:

  • Check for JavaScript aggregation issues
  • Disable JavaScript optimization temporarily
  • Review module load order
  • Check for conflicting page_attachments implementations
  • Disable other analytics modules temporarily
  • Review Recent log messages: /admin/reports/dblog

Cache Issues

Symptoms: GTM changes not appearing after updates

Solutions:

  • Clear all Drupal caches: drush cr
  • Clear browser cache
  • Disable page cache for testing
  • Check cache tags on container configuration
  • Verify cache contexts
  • Use Cache Metadata module for debugging

Template Override Issues

Symptoms: GTM code removed after theme updates

Solutions:

  • Use module-based implementation instead
  • Create custom sub-theme
  • Document theme modifications
  • Use hook implementations instead of template edits
  • Version control theme files
  • Test after theme updates

Permissions Issues

Symptoms: GTM only appears for certain users

Solutions:

  • Review role-based visibility settings
  • Check user permissions for GTM module
  • Verify container conditions
  • Test with different user roles
  • Review access control settings
  • Check for user-based caching issues

Advanced Implementation

Multi-Site GTM Configuration

For Drupal multi-site installations:

// In sites/site1/settings.php
$config['google_tag.container.default']['container_id'] = 'GTM-SITE1';

// In sites/site2/settings.php
$config['google_tag.container.default']['container_id'] = 'GTM-SITE2';

Custom Entity Type Tracking

/**
 * Track custom entity views.
 */
function mymodule_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($entity->getEntityTypeId() === 'custom_entity' && $view_mode === 'full') {
    $build['#attached']['html_head'][] = [
      [
        '#type' => 'html_tag',
        '#tag' => 'script',
        '#value' => 'dataLayer.push({
          "event": "customEntityView",
          "entityId": "' . $entity->id() . '",
          "entityLabel": "' . $entity->label() . '"
        });',
      ],
      'custom_entity_tracking',
    ];
  }
}

Webform Integration

Track Drupal Webform submissions:

/**
 * Implements hook_webform_submission_insert().
 */
function mymodule_webform_submission_insert(WebformSubmissionInterface $submission) {
  $webform = $submission->getWebform();
  $data = [
    'event' => 'webformSubmission',
    'formId' => $webform->id(),
    'formTitle' => $webform->label(),
  ];

  // Attach to next page load
  \Drupal::service('session')->set('gtm_webform_data', $data);
}

Commerce Enhanced Ecommerce

Full Drupal Commerce integration:

/**
 * Add to cart event.
 */
function mymodule_commerce_cart_entity_add(CartInterface $cart, OrderItemInterface $order_item, $quantity) {
  $purchased_entity = $order_item->getPurchasedEntity();

  $data = [
    'event' => 'addToCart',
    'ecommerce' => [
      'currencyCode' => $order_item->getTotalPrice()->getCurrencyCode(),
      'add' => [
        'products' => [[
          'name' => $purchased_entity->label(),
          'id' => $purchased_entity->id(),
          'price' => $order_item->getUnitPrice()->getNumber(),
          'quantity' => $quantity,
        ]],
      ],
    ],
  ];

  \Drupal::service('session')->set('gtm_add_to_cart', $data);
}

Performance Optimization

Async Loading

The Google Tag module loads GTM asynchronously by default. Verify in configuration:

Configuration > System > Google Tag Manager > Advanced > Include script as fallback

Conditional Loading

Load GTM only where needed:

// In custom module
function mymodule_page_attachments_alter(array &$attachments) {
  $route_name = \Drupal::routeMatch()->getRouteName();

  // Don't load GTM on admin pages
  if (strpos($route_name, 'admin') === 0) {
    foreach ($attachments['#attached']['html_head'] as $key => $value) {
      if (isset($value[1]) && $value[1] === 'google_tag_manager') {
        unset($attachments['#attached']['html_head'][$key]);
      }
    }
  }
}

Cache Optimization

Configure appropriate cache tags:

$build['#cache']['tags'][] = 'google_tag_container';
$build['#cache']['contexts'][] = 'user.roles';
// SYS.FOOTER