Google Tag Manager with DatoCMS | OpsBlu Docs

Google Tag Manager with DatoCMS

Overview of integrating Google Tag Manager with DatoCMS headless CMS for flexible tag management and tracking.

Google Tag Manager (GTM) provides a flexible way to manage tracking tags on your DatoCMS-powered website. Since DatoCMS is headless, GTM is implemented in your frontend framework.

Why Use GTM with DatoCMS

Centralized Tag Management:

  • Manage all tracking tags in one place
  • Deploy tags without code changes
  • Version control for tag configurations
  • Easy A/B testing setup

DatoCMS-Specific Benefits:

  • Track content models without hardcoding
  • Dynamic data layer from GraphQL queries
  • Flexible event tracking for modular content
  • Multi-locale tracking support

Team Collaboration:

  • Marketing team can manage tags independently
  • Developers set up data layer once
  • Non-technical users can add tracking
  • Reduced dependency on development team

How GTM Works with DatoCMS

  1. Frontend Framework → Fetches content from DatoCMS GraphQL API
  2. Data Layer → Pushes DatoCMS content metadata
  3. GTM Container → Reads data layer
  4. Tags → Fire based on data layer values and triggers
  5. Analytics Platforms → Receive tracking data

GTM Container Setup

Install GTM container in your DatoCMS frontend:

<!-- Google Tag Manager (in <head>) -->
<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-XXXXXX');</script>
<!-- End Google Tag Manager -->

<!-- Google Tag Manager (noscript) (after <body>) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

Data Layer Structure for DatoCMS

Basic Page Load

window.dataLayer = window.dataLayer || [];
dataLayer.push({
  'event': 'datocms_page_view',
  'contentId': record.id,
  'contentType': record._modelApiKey,
  'contentTitle': record.title,
  'contentSlug': record.slug,
  'publishedAt': record._publishedAt,
  'updatedAt': record._updatedAt,
  'locale': record._locales?.[0] || 'en',
});

Content with Categories/Tags

dataLayer.push({
  'event': 'datocms_content_loaded',
  'content': {
    'id': post.id,
    'type': post._modelApiKey,
    'title': post.title,
    'category': post.category?.title,
    'tags': post.tags?.map(t => t.title).join(','),
    'author': post.author?.name,
  }
});

E-commerce Products

dataLayer.push({
  'event': 'datocms_product_view',
  'ecommerce': {
    'items': [{
      'item_id': product.id,
      'item_name': product.title,
      'item_category': product.category?.title,
      'price': product.price,
      'item_brand': product.brand?.name,
    }]
  }
});

Common GTM Tags for DatoCMS

GA4 Configuration Tag

Tag Type: Google Analytics: GA4 Configuration Trigger: All Pages Configuration:

  • Measurement ID: G-XXXXXXXXXX
  • Fields to Set:
    • content_id: \{\{DL - contentId\}\}
    • content_type: \{\{DL - contentType\}\}

GA4 Event Tag

Tag Type: Google Analytics: GA4 Event Trigger: Custom Event - datocms_content_view Event Name: content_view Event Parameters:

  • content_id: \{\{DL - contentId\}\}
  • content_type: \{\{DL - contentType\}\}
  • content_title: \{\{DL - contentTitle\}\}

Meta Pixel Tag

Tag Type: Custom HTML Trigger: All Pages HTML:

<script>
  fbq('track', 'PageView', {
    content_id: {{DL - contentId}},
    content_type: {{DL - contentType}},
  });
</script>

Framework-Specific Implementation

Next.js (App Router)

// app/components/GTM.tsx
'use client'

import Script from 'next/script'

export function GoogleTagManager() {
  const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID

  if (!GTM_ID) return null

  return (
    <>
      <Script
        id="gtm-script"
        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!='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_ID}');
          `,
        }}
      />
    </>
  )
}

// app/components/GTMDataLayer.tsx
'use client'

import { useEffect } from 'react'

export function GTMDataLayer({ record }: { record: any }) {
  useEffect(() => {
    window.dataLayer = window.dataLayer || []
    window.dataLayer.push({
      event: 'datocms_page_view',
      contentId: record.id,
      contentType: record._modelApiKey,
      contentTitle: record.title,
    })
  }, [record])

  return null
}

Gatsby

// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-google-tagmanager',
      options: {
        id: process.env.GTM_ID,
        includeInDevelopment: false,
        defaultDataLayer: { platform: 'gatsby' },
      },
    },
  ],
}

// In component
useEffect(() => {
  if (typeof window !== 'undefined') {
    window.dataLayer.push({
      event: 'datocms_content_view',
      contentId: post.originalId,
      contentType: 'blog_post',
    })
  }
}, [post])

GTM Variables

Create these variables in GTM:

DL - contentId

  • Type: Data Layer Variable
  • Data Layer Variable Name: contentId

DL - contentType

  • Type: Data Layer Variable
  • Data Layer Variable Name: contentType

DL - contentTitle

  • Type: Data Layer Variable
  • Data Layer Variable Name: contentTitle

DL - locale

  • Type: Data Layer Variable
  • Data Layer Variable Name: locale

GTM Triggers

DatoCMS Content View

  • Type: Custom Event
  • Event name: datocms_content_view

Specific Content Type

  • Type: Custom Event
  • Event name: datocms_page_view
  • Fires on: Some Custom Events
  • Condition: contentType equals blog_post

Multi-locale Trigger

  • Type: Custom Event
  • Event name: datocms_page_view
  • Condition: locale equals en

Testing GTM with DatoCMS

  1. Preview Mode: Use GTM Preview mode to see data layer
  2. Check Data Layer: Verify DatoCMS data is pushed correctly
  3. Test Tags: Ensure tags fire on correct triggers
  4. Verify Events: Check events in GA4 DebugView

Best Practices

  1. Consistent Data Layer: Use same structure across all pages
  2. Content ID: Always include DatoCMS record ID
  3. Model Type: Track content model for segmentation
  4. Preview Exclusion: Don't track DatoCMS preview mode
  5. Version Control: Document GTM container changes

Next Steps