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.
useScriptGoogleAnalytics() composable instead.Script Stats
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.
<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
| Issue | Cause | Solution |
|---|---|---|
| Script blocked by ad blocker | Custom domain detected as tracker | Use a non-obvious subdomain name (avoid gtm, analytics, tracking) |
| Cookies expire after 7 days in Safari | ITP treats subdomain as third-party | Use same-origin setup or implement cookie keeper |
| Preview mode not working | Missing or incorrect auth/preview tokens | Copy tokens from GTM: Admin > Environments > Get Snippet |
| CORS errors | Server container misconfigured | Ensure your server container allows requests from your domain |
gtm.js returns 404 | Incorrect path mapping | Verify 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:
Option 1: Using defaultConsent in nuxt.config (Recommended)
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.Consent Mode v2 Signals
| Signal | Purpose |
|---|---|
ad_storage | Cookies for advertising |
ad_user_data | Send user data to Google for ads |
ad_personalization | Personalized ads (remarketing) |
analytics_storage | Cookies for analytics |
Updating Consent
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>