Installing Google Tag Manager on Storyblok with Nuxt and | OpsBlu Docs

Installing Google Tag Manager on Storyblok with Nuxt and

Complete guide to setting up GTM on Storyblok-powered sites for centralized tag and analytics management

Google Tag Manager (GTM) provides centralized management of tracking tags on Storyblok-powered websites. This guide covers GTM implementation for Nuxt.js, Next.js, and custom implementations.


Prerequisites

Before you begin:

  • Have a Google Tag Manager account created
  • Know your GTM Container ID (format: GTM-XXXXXXX)
  • Have a Storyblok space set up
  • Be using Nuxt.js, Next.js, or another JavaScript framework

Create GTM Container

  1. Log in to Google Tag Manager
  2. Click Create Account (if new) or Add Container
  3. Enter:
    • Account Name: Your company name
    • Container Name: Your website name
    • Target Platform: Web
  4. Accept Terms of Service
  5. Copy your Container ID (GTM-XXXXXXX)

Method 1: Nuxt.js with Storyblok

Step 1: Install GTM Module

npm install @nuxtjs/gtm

Step 2: Configure nuxt.config.ts

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@storyblok/nuxt',
    '@nuxtjs/gtm',
  ],

  storyblok: {
    accessToken: process.env.STORYBLOK_TOKEN,
  },

  gtm: {
    id: process.env.NUXT_PUBLIC_GTM_ID,
    enabled: process.env.NODE_ENV === 'production',
    debug: process.env.NODE_ENV === 'development',
    loadScript: true,
    enableRouterSync: true,
  },

  runtimeConfig: {
    public: {
      gtmId: process.env.NUXT_PUBLIC_GTM_ID,
    },
  },
});

Step 3: Environment Variables

Create .env:

STORYBLOK_TOKEN=your-storyblok-token
NUXT_PUBLIC_GTM_ID=GTM-XXXXXXX

Method 2: Next.js with Storyblok

Step 1: Create GTM Component

Create components/GoogleTagManager.js:

'use client';

import Script from 'next/script';

export default function GoogleTagManager({ gtmId }) {
  return (
    <>
      <Script id="google-tag-manager" strategy="afterInteractive">
        {`
          (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>
    </>
  );
}

Step 2: Create GTM NoScript Component

// components/GoogleTagManagerNoScript.js
export default function GoogleTagManagerNoScript({ gtmId }) {
  return (
    <noscript>
      <iframe
        src={`https://www.googletagmanager.com/ns.html?id=${gtmId}`}
        height="0"
        width="0"
        style={{ display: 'none', visibility: 'hidden' }}
      />
    </noscript>
  );
}

Step 3: Add to Root Layout

// app/layout.js
import { storyblokInit, apiPlugin } from '@storyblok/react';
import GoogleTagManager from '@/components/GoogleTagManager';
import GoogleTagManagerNoScript from '@/components/GoogleTagManagerNoScript';

storyblokInit({
  accessToken: process.env.NEXT_PUBLIC_STORYBLOK_TOKEN,
  use: [apiPlugin],
});

const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID;

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        {GTM_ID && <GoogleTagManager gtmId={GTM_ID} />}
      </head>
      <body>
        {GTM_ID && <GoogleTagManagerNoScript gtmId={GTM_ID} />}
        {children}
      </body>
    </html>
  );
}

Initialize Data Layer for Storyblok

Nuxt 3

Create plugins/storyblok-data-layer.client.ts:

// plugins/storyblok-data-layer.client.ts
export default defineNuxtPlugin(() => {
  const route = useRoute();

  // Initialize dataLayer
  window.dataLayer = window.dataLayer || [];

  // Track Storyblok stories
  const pushStoryData = (story: any) => {
    window.dataLayer.push({
      event: 'storyblok_story_loaded',
      storyblok: {
        storyId: story.id,
        storyName: story.name,
        storySlug: story.slug,
        component: story.content.component,
        createdAt: story.created_at,
        publishedAt: story.published_at,
      },
    });
  };

  return {
    provide: {
      pushStoryData,
    },
  };
});

Usage in page:

<!-- pages/[...slug].vue -->
<script setup>
const { slug } = useRoute().params;
const story = await useAsyncStoryblok(slug.join('/'));
const { $pushStoryData } = useNuxtApp();

onMounted(() => {
  if (story.value) {
    $pushStoryData(story.value);
  }
});
</script>

Track Client-Side Navigation

Nuxt 3

The @nuxtjs/gtm module automatically tracks route changes.

Next.js

Create components/GTMPageView.js:

'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';

export function GTMPageView() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (typeof window.dataLayer !== 'undefined') {
      window.dataLayer.push({
        event: 'pageview',
        page: pathname + searchParams.toString(),
      });
    }
  }, [pathname, searchParams]);

  return null;
}

Configure GTM Container

Basic GA4 Tag Setup

  1. In GTM, go to Tags > New
  2. Tag Configuration:
  3. Triggering:
    • Trigger: All Pages
  4. Save

Create Data Layer Variables

  1. Go to Variables > New
  2. Variable Configuration:
  3. Create these variables:
Variable Name Data Layer Variable Name
Storyblok Story ID storyblok.storyId
Storyblok Component storyblok.component
Storyblok Story Name storyblok.storyName

Testing & Validation

1. GTM Preview Mode

  1. In GTM, click Preview
  2. Enter your Storyblok site URL
  3. Click Connect
  4. Verify container loads and tags fire

2. Browser Console

// Check dataLayer
console.log(window.dataLayer);

Next Steps


Additional Resources