Scripts

Google Tag Manager

Google Tag Manager is a tag management system that allows you to quickly and easily update tags and code snippets on your website or mobile app, such as those intended for traffic analysis and marketing optimization.

You may not need Google Tag Manager with Nuxt Scripts. GTM is 82kb and will slow down your site. Nuxt Scripts provides many features you can easily implement within your Nuxt app. If you're using GTM for Google Analytics, you can use the useScriptGoogleAnalytics() composable instead.

Script Stats

Transfer
106.1 KB
Decoded
295.0 KB
Loading
Dynamic
First-Party
Supported
Bundling
Supported
Privacy
No data collected
Tracked Data
Tag Injection

Nuxt Config Setup

The simplest way to load Google Tag Manager globally in your Nuxt App is to use Nuxt config. Alternatively you can directly use the useScriptGoogleTagManager composable.

export default defineNuxtConfig({
  scripts: {
    registry: {
      googleTagManager: {
        id: 'GTM-XXXXXX'
      }
    }
  }
})

useScriptGoogleTagManager

The useScriptGoogleTagManager composable lets you have fine-grain control over when and how Google Tag Manager is loaded on your site.

const { proxy } = useScriptGoogleTagManager()

proxy.dataLayer.push({ event: 'conversion', value: 1 })

Please follow the Registry Scripts guide to learn more about advanced usage.

First-Party Mode

This script supports First-Party Mode which routes all traffic through your domain for improved privacy and ad blocker bypass.

export default defineNuxtConfig({
  scripts: {
    firstParty: true,
    registry: {
      googleTagManager: { id: 'GTM-XXXXXX'}
    }
  }
})

To opt-out for this specific script:

useScriptGoogleTagManager({
  id: 'GTM-XXXXXX',
  scriptOptions: {
    firstParty: false
  }
})

Example

Using Google Tag Manager only in production while using the proxy to send events.

ConversionButton.vue
<script setup lang="ts">
const { proxy } = useScriptGoogleTagManager()

// noop in development, ssr
// just works in production, client
function handleAction() {
  proxy.dataLayer.push({ event: 'conversion', value: 1 })
}
</script>

<template>
  <div>
    <button @click="handleAction">
      Send Event
    </button>
  </div>
</template>

Guide: Sending Page Events

If you'd like to manually send page events to Google Tag Manager, you can use the proxy with the useScriptEventPage() composable. This composable will trigger the provided function on route change after the page title has been updated.

const { proxy } = useScriptGoogleTagManager({
  id: 'YOUR_ID' // id is only needed if you haven't configured globally
})

useScriptEventPage(({ title, path }) => {
  // triggered on route change after title is updated
  proxy.dataLayer.push({
    event: 'pageview',
    title,
    path
  })
})

GoogleTagManagerApi

interface GoogleTagManagerApi {
  dataLayer: Record<string, any>[]
  google_tag_manager: GoogleTagManager
}

Config Schema

You must provide the options when setting up the script for the first time.

/**
 * GTM configuration options with improved documentation
 */
export const GoogleTagManagerOptions = object({
    /** GTM container ID (format: GTM-XXXXXX) */
    id: string(),

    /** Optional dataLayer variable name */
    l: optional(string()),

    /** Authentication token for environment-specific container versions */
    auth: optional(string()),

    /** Preview environment name */
    preview: optional(string()),

    /** Forces GTM cookies to take precedence when true */
    cookiesWin: optional(union([boolean(), literal('x')])),

    /** Enables debug mode when true */
    debug: optional(union([boolean(), literal('x')])),

    /** No Personal Advertising - disables advertising features when true */
    npa: optional(union([boolean(), literal('1')])),

    /** Custom dataLayer name (alternative to "l" property) */
    dataLayer: optional(string()),

    /** Environment name for environment-specific container */
    envName: optional(string()),

    /** Referrer policy for analytics requests */
    authReferrerPolicy: optional(string()),

    /** Default consent settings for GTM */
    defaultConsent: optional(record(string(), union([string(), number()]))),
  })

Options types

type GoogleTagManagerInput = typeof GoogleTagManagerOptions & { onBeforeGtmStart?: (gtag: Gtag) => void }

Examples

Server-Side GTM Setup

Server-side GTM moves tag execution to your server for better privacy, performance (~500ms faster), and ad-blocker bypass.

Prerequisites: Server-side GTM container, hosting (Cloud Run / Docker), and a custom domain.

Configuration

Override the script source with your custom domain:

// nuxt.config.ts
export default defineNuxtConfig({
  scripts: {
    registry: {
      googleTagManager: {
        id: 'GTM-XXXXXX',
        scriptInput: {
          src: 'https://gtm.example.com/gtm.js'
        }
      }
    }
  }
})

For environment tokens (auth, preview), find them in GTM: Admin > Environments > Get Snippet.

Troubleshooting

IssueCauseSolution
Script blocked by ad blockerCustom domain detected as trackerUse a non-obvious subdomain name (avoid gtm, analytics, tracking)
Cookies expire after 7 days in SafariITP treats subdomain as third-partyUse same-origin setup or implement cookie keeper
Preview mode not workingMissing or incorrect auth/preview tokensCopy tokens from GTM: Admin > Environments > Get Snippet
CORS errorsServer container misconfiguredEnsure your server container allows requests from your domain
gtm.js returns 404Incorrect path mappingVerify your CDN/proxy routes /gtm.js to the container

For infrastructure setup, see Cloud Run or Docker guides.

Configuring GTM before it starts

useScriptGoogleTagManager() initializes Google Tag Manager by itself. This means it pushes the js, config and the gtm.start events for you.

If you need to configure GTM before it starts, for example setting the consent mode, you have two options:

If you're configuring GTM in nuxt.config, use the defaultConsent option. See the Default consent mode example above.

Option 2: Using onBeforeGtmStart callback

If you're calling useScriptGoogleTagManager() with the ID directly in a component (not in nuxt.config), use the onBeforeGtmStart hook which runs right before the gtm.start event is pushed.

onBeforeGtmStart only works when the GTM ID is passed directly to useScriptGoogleTagManager(), not when configured globally in nuxt.config. For global config, use the defaultConsent option instead.
Try the live Cookie Consent Example or Granular Consent Example on StackBlitz.
SignalPurpose
ad_storageCookies for advertising
ad_user_dataSend user data to Google for ads
ad_personalizationPersonalized ads (remarketing)
analytics_storageCookies for analytics

When the user accepts, call gtag('consent', 'update', ...):

function acceptCookies() {
  window.gtag?.('consent', 'update', {
    ad_storage: 'granted',
    ad_user_data: 'granted',
    ad_personalization: 'granted',
    analytics_storage: 'granted',
  })
}

To block GTM entirely until consent, combine with useScriptTriggerConsent().

<script setup lang="ts">
const consent = useState('consent', () => 'denied')

const { proxy } = useScriptGoogleTagManager({
  onBeforeGtmStart: (gtag) => {
    // set default consent state to denied
    gtag('consent', 'default', {
      'ad_user_data': 'denied',
      'ad_personalization': 'denied',
      'ad_storage': 'denied',
      'analytics_storage': 'denied',
      'wait_for_update': 500,
    })

    // if consent was already given, update gtag accordingly
    if (consent.value === 'granted') {
      gtag('consent', 'update', {
        ad_user_data: consent.value,
        ad_personalization: consent.value,
        ad_storage: consent.value,
        analytics_storage: consent.value
      })
    }
  }
})

// push pageview events to dataLayer
useScriptEventPage(({ title, path }) => {
  proxy.dataLayer.push({
    event: 'pageview',
    title,
    path
  })
})
</script>