From 6dfd52592c382d1690cc93020633f30c9758a317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kersten?= Date: Tue, 9 Sep 2025 12:06:56 +0200 Subject: [PATCH 1/3] fix(gtm): move dataLayer initialization into gtag function - Fix dataLayer initialization by moving it inside gtag function - Add onBeforeGtmStart callback option for custom initialization - Ensure dataLayer exists before each gtag call to prevent errors - Add documentation example showing consent mode configuration Fixes potential race condition where dataLayer might not be initialized when onBeforeGtmStart callback is executed. --- .../scripts/tracking/google-tag-manager.md | 34 +++++++++++++++++++ src/runtime/registry/google-tag-manager.ts | 7 ++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/content/scripts/tracking/google-tag-manager.md b/docs/content/scripts/tracking/google-tag-manager.md index 2b39a2aa..f0328c5e 100644 --- a/docs/content/scripts/tracking/google-tag-manager.md +++ b/docs/content/scripts/tracking/google-tag-manager.md @@ -198,3 +198,37 @@ function sendConversion() { `useScriptGoogleTagManager` initialize 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](https://developers.google.com/tag-platform/security/guides/consent?consentmode=basic). You can use the `onBeforeGtmStart` hook which is run right before we push the `gtm.start` event into the dataLayer. + +```vue +const { proxy } = useScriptGoogleTagManager({ + onBeforeGtmStart: () => { + // 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 + }) +}) +``` diff --git a/src/runtime/registry/google-tag-manager.ts b/src/runtime/registry/google-tag-manager.ts index 96ab5506..027996b2 100644 --- a/src/runtime/registry/google-tag-manager.ts +++ b/src/runtime/registry/google-tag-manager.ts @@ -159,11 +159,12 @@ export function useScriptGoogleTagManager( clientInit: import.meta.server ? undefined : () => { - // Initialize dataLayer if it doesn't exist - (window as any)[dataLayerName] = (window as any)[dataLayerName] || [] - // Create gtag function function gtag(...args: any[]) { + // Initialize dataLayer if it doesn't exist + (window as any)[dataLayerName] = (window as any)[dataLayerName] || [] + + // Push arguments to dataLayer (window as any)[dataLayerName].push(args) } From 9ecef17ac5332b6298b6d1f7ce0a8945e5160d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kersten?= Date: Wed, 10 Sep 2025 23:46:45 +0200 Subject: [PATCH 2/3] fix(gtm): fix onBeforeGtmStart gtag function using arguments object - Fix gtag function in onBeforeGtmStart hook to use native arguments object - GTM DataLayer expects Arguments object, not Array for proper event processing - Enables consent settings to be correctly processed before GTM initialization - Add ESLint disable comment for prefer-rest-params rule --- src/runtime/registry/google-tag-manager.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/runtime/registry/google-tag-manager.ts b/src/runtime/registry/google-tag-manager.ts index 027996b2..23fc2905 100644 --- a/src/runtime/registry/google-tag-manager.ts +++ b/src/runtime/registry/google-tag-manager.ts @@ -159,13 +159,14 @@ export function useScriptGoogleTagManager( clientInit: import.meta.server ? undefined : () => { - // Create gtag function - function gtag(...args: any[]) { - // Initialize dataLayer if it doesn't exist - (window as any)[dataLayerName] = (window as any)[dataLayerName] || [] + // Initialize dataLayer if it doesn't exist + (window as any)[dataLayerName] = (window as any)[dataLayerName] || [] - // Push arguments to dataLayer - (window as any)[dataLayerName].push(args) + // Create gtag function + function gtag() { + // Pushing arguments to dataLayer is necessary for GTM to process events + // eslint-disable-next-line prefer-rest-params + (window as any)[dataLayerName].push(arguments) } // Allow custom initialization From 1e2e7d06dfcc3e4e5ce3f76519d7c573777da3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kersten?= Date: Thu, 11 Sep 2025 00:07:40 +0200 Subject: [PATCH 3/3] docs(gtm): fix onBeforeGtmStart hook example missing gtag parameter The example code was missing the gtag parameter in the onBeforeGtmStart callback function, which would cause a ReferenceError when trying to use gtag() for --- docs/content/scripts/tracking/google-tag-manager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/scripts/tracking/google-tag-manager.md b/docs/content/scripts/tracking/google-tag-manager.md index f0328c5e..f35378f1 100644 --- a/docs/content/scripts/tracking/google-tag-manager.md +++ b/docs/content/scripts/tracking/google-tag-manager.md @@ -201,7 +201,7 @@ If you need to configure GTM before it starts. For example, [setting the consent ```vue const { proxy } = useScriptGoogleTagManager({ - onBeforeGtmStart: () => { + onBeforeGtmStart: (gtag) => { // set default consent state to denied gtag('consent', 'default', { 'ad_user_data': 'denied',