@@ -8,146 +8,60 @@ import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
88const GTM_ID = "AW-17391954825"
99
1010/**
11- * Google Analytics Provider with Consent Mode v2
12- * Implements cookieless pings and advanced consent management
11+ * Google Analytics Provider
12+ * Implements Google's standard gtag.js loading pattern
1313 */
1414export function GoogleAnalyticsProvider ( { children } : { children : React . ReactNode } ) {
1515 const [ shouldLoad , setShouldLoad ] = useState ( false )
1616
1717 useEffect ( ( ) => {
18- // Initialize consent defaults BEFORE loading gtag.js (required for Consent Mode v2)
19- initializeConsentDefaults ( )
20-
2118 // Check initial consent status
2219 if ( hasConsent ( ) ) {
2320 setShouldLoad ( true )
24- updateConsentGranted ( )
2521 }
2622
2723 // Listen for consent changes
2824 const unsubscribe = onConsentChange ( ( consented ) => {
29- if ( consented ) {
30- if ( ! shouldLoad ) {
31- setShouldLoad ( true )
32- }
33- updateConsentGranted ( )
34- } else {
35- updateConsentDenied ( )
25+ if ( consented && ! shouldLoad ) {
26+ setShouldLoad ( true )
3627 }
3728 } )
3829
3930 return unsubscribe
40- // eslint-disable-next-line react-hooks/exhaustive-deps -- shouldLoad intentionally omitted to prevent re-initialization loop
41- } , [ ] )
31+ } , [ shouldLoad ] )
4232
43- const initializeConsentDefaults = ( ) => {
44- // Set up consent defaults before gtag loads (Consent Mode v2 requirement )
33+ useEffect ( ( ) => {
34+ // Initialize dataLayer as early as possible (Google's recommended pattern )
4535 if ( typeof window !== "undefined" ) {
4636 window . dataLayer = window . dataLayer || [ ]
47- window . gtag = function ( ...args : GtagArgs ) {
48- window . dataLayer . push ( args )
49- }
50-
51- // Set default consent state to 'denied' with cookieless pings enabled
52- window . gtag ( "consent" , "default" , {
53- ad_storage : "denied" ,
54- ad_user_data : "denied" ,
55- ad_personalization : "denied" ,
56- analytics_storage : "denied" ,
57- functionality_storage : "denied" ,
58- personalization_storage : "denied" ,
59- security_storage : "granted" , // Always granted for security
60- wait_for_update : 500 , // Wait 500ms for consent before sending data
61- } )
62-
63- // Enable cookieless pings for Google Ads
64- window . gtag ( "set" , "url_passthrough" , true )
65- }
66- }
67-
68- const updateConsentGranted = ( ) => {
69- // User accepted cookies - update consent to granted
70- if ( typeof window !== "undefined" && window . gtag ) {
71- window . gtag ( "consent" , "update" , {
72- ad_storage : "granted" ,
73- ad_user_data : "granted" ,
74- ad_personalization : "granted" ,
75- analytics_storage : "granted" ,
76- functionality_storage : "granted" ,
77- personalization_storage : "granted" ,
78- } )
79- }
80- }
81-
82- const updateConsentDenied = ( ) => {
83- // User declined cookies - keep consent denied (cookieless pings still work)
84- if ( typeof window !== "undefined" && window . gtag ) {
85- window . gtag ( "consent" , "update" , {
86- ad_storage : "denied" ,
87- ad_user_data : "denied" ,
88- ad_personalization : "denied" ,
89- analytics_storage : "denied" ,
90- functionality_storage : "denied" ,
91- personalization_storage : "denied" ,
92- } )
9337 }
94- }
95-
96- // Always render scripts (Consent Mode v2 needs gtag loaded even without consent)
97- // Cookieless pings will work with denied consent
38+ } , [ ] )
9839
9940 return (
10041 < >
101- { /* Google tag (gtag.js) - Loads immediately for Consent Mode v2 */ }
102- < Script
103- src = { `https://www.googletagmanager.com/gtag/js?id=${ GTM_ID } ` }
104- strategy = "afterInteractive"
105- onLoad = { ( ) => {
106- // Initialize gtag config after script loads
107- if ( typeof window !== "undefined" && window . gtag ) {
108- window . gtag ( "js" , new Date ( ) )
109- window . gtag ( "config" , GTM_ID )
110- }
111- } }
112- />
42+ { shouldLoad && (
43+ < >
44+ { /* Google tag (gtag.js) */ }
45+ < Script src = { `https://www.googletagmanager.com/gtag/js?id=${ GTM_ID } ` } strategy = "afterInteractive" />
46+ < Script id = "google-analytics" strategy = "afterInteractive" >
47+ { `
48+ window.dataLayer = window.dataLayer || [];
49+ function gtag(){dataLayer.push(arguments);}
50+ gtag('js', new Date());
51+ gtag('config', '${ GTM_ID } ');
52+ ` }
53+ </ Script >
54+ </ >
55+ ) }
11356 { children }
11457 </ >
11558 )
11659}
11760
118- // Type definitions for Google Analytics with Consent Mode v2
119- type ConsentState = "granted" | "denied"
120-
121- interface ConsentParams {
122- ad_storage ?: ConsentState
123- ad_user_data ?: ConsentState
124- ad_personalization ?: ConsentState
125- analytics_storage ?: ConsentState
126- functionality_storage ?: ConsentState
127- personalization_storage ?: ConsentState
128- security_storage ?: ConsentState
129- wait_for_update ?: number
130- }
131-
132- type GtagArgs =
133- | [ "js" , Date ]
134- | [ "config" , string , GtagConfig ?]
135- | [ "event" , string , GtagEventParameters ?]
136- | [ "consent" , "default" | "update" , ConsentParams ]
137- | [ "set" , string , unknown ]
138-
139- interface GtagConfig {
140- [ key : string ] : unknown
141- }
142-
143- interface GtagEventParameters {
144- [ key : string ] : unknown
145- }
146-
14761// Declare global types for TypeScript
14862declare global {
14963 interface Window {
150- dataLayer : GtagArgs [ ]
151- gtag : ( ...args : GtagArgs ) => void
64+ dataLayer : unknown [ ]
65+ gtag : ( ...args : unknown [ ] ) => void
15266 }
15367}
0 commit comments