Scripts

Google Tag Manager

Last updated by Harlan Wilton in chore: lint.

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.4 KB
Decoded
295.9 KB
Loading
Dynamic
First-Party
Supported
Bundling
Supported
Privacy
No data collected
Tracked Data
Tag Injection

View source

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'}
    }
  }
})

Example

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

<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 triggers the provided function on route change after Nuxt updates the page title.

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
  })
})
idstring required

GTM container ID (format: GTM-XXXXXX)

lstring = 'dataLayer'

Optional dataLayer variable name

authstring

Authentication token for environment-specific container versions

previewstring

Preview environment name

cookiesWinboolean | 'x'
debugboolean | 'x'

Enables debug mode when true

npaboolean | '1'

No Personal Advertising - disables advertising features when true

dataLayerstring
envNamestring

Environment name for environment-specific container

authReferrerPolicystring
defaultConsentRecord<string, string | number>

Default consent settings for GTM

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