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.
Google Tag Manager

View source

Nuxt Config Setup

Add this to your nuxt.config.ts to load Google Tag Manager globally. Alternatively you can use the useScriptGoogleTagManager composable for more control.

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

This config automatically enables first-party mode (bundle). See below to customise.

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: Privacy Focused Proxy

No extra config needed. The script is bundled and served from your domain instead of a third-party CDN, eliminating an extra DNS lookup and improving load times. Learn more.

Mode
Bundle
export default defineNuxtConfig({
  scripts: {
    // ✅ First-party mode: bundled
    registry: {
      googleTagManager: {
        id: 'GTM-XXXXXX',
        trigger: 'onNuxtReady',
      },
    },
  },
})

Example

Using Google Tag Manager in a component.

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

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