Next.js Discord

Discord Forum

Consent Google Analytics Cookies

Unanswered
Barbary Lion posted this in #help-forum
Open in Discord
Barbary LionOP
Hello, I want to add google analytics on my website. I came across @next/third-parties/google but I didn't found any way to implement a consent message to let the user decide wether they want to accept or decline the cookies.

2 Replies

Barbary LionOP
If I uncomment the code, I get the error, that gtag is not a function. How can I solve it? If I run the build command, it tells me that I cannot use useSearchParams without wrapping it inside a Suspense component. But if I wrap it, I get a hydration error...

# GoogleAnalytics.tsx
"use client";

// import { useSearchParams, usePathname } from "next/navigation";
import { GoogleTagManager } from "@next/third-parties/google";
import { useEffect, useState } from "react";

type GoogleAnalyticsProps = {
    gtmId: string;
};

export const GoogleAnalytics = ({ gtmId }: GoogleAnalyticsProps) => {
    const [allowGoogleTracking, setAllowGoogleTracking] = useState(false);

    // const pathname = usePathname();
    // const searchParams = useSearchParams();

    useEffect(() => {
        const allowTracking = localStorage.getItem("allowGoogleTracking") ?? "false";
        if (allowTracking === "true") setAllowGoogleTracking(true);

        // const url = pathname + searchParams.toString();

        // console.log(url);

        // window.gtag("config", gtmId, {
        //     page_path: url
        // });
    }, []);

    return allowGoogleTracking && <GoogleTagManager gtmId={gtmId} />;
};


# layout.tsx
import "@/app/globals.scss";

import { Inter as FontSans } from "next/font/google";

import { TailwindIndicator } from "@/components/TailwindIndicator";
import { generateMetadata, generateViewport } from "@/lib/meta";
import { GoogleAnalytics } from "@/components/GoogleAnalytics";
import { CookieBanner } from "@/components/CookieBanner";
import { ScrollTop } from "@/components/ScrollTop";
import { Toaster } from "@/components/ui/toaster";
import { cn } from "@/lib/utils";

const fontSans = FontSans({
    subsets: ["latin"],
    variable: "--font-inter"
});

export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
    return (
        <html lang="en">
            <head>
                <GoogleAnalytics gtmId={process.env.NEXT_PUBLIC_GTM_ID} />
            </head>

            <body
                suppressHydrationWarning={true}
                className={cn("no-scrollbar h-dvh min-h-dvh overflow-y-auto bg-background font-sans antialiased", fontSans.variable)}
            >
                <div id="top" className="relative flex h-dvh min-h-dvh flex-col">
                    {children}
                </div>

                <TailwindIndicator />
                <CookieBanner />
                <ScrollTop />
                <Toaster />
            </body>
        </html>
    );
}

export const metadata = generateMetadata();

export const viewport = generateViewport();
Highlander
I've strungled with cookie consent on nextjs also, I still was not able to manage consent (setting default, and update) using @next/third-party/google so this is how I manage consnet on my Next.js 15 project. Tested with Google Tag Assistant
'use client';
import Script from 'next/script';
import { useEffect, useState } from 'react';

export default function GoogleTag({ GA_ID }: { GA_ID: string }) {

  const [consent, setConsent] = useState<string | null>(null);

  useEffect(() => {

    // Set the consent value after the client loaded to avoid errors.
    setConsent(localStorage.getItem('cookie') === "true" ? 'granted' : 'denied');

  }, [GA_ID]);

  if (consent === null) return null; 

  return (
    <>
      <Script
        strategy="afterInteractive"
        src={https://www.googletagmanager.com/gtag/js?id=${GA_ID}}
      />
      <Script
        id="google-tag-manager"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: 
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('consent', 'default', {
              'ad_storage': '${consent}',
              'ad_user_data': '${consent}',
              'ad_personalization': '${consent}',
              'analytics_storage': '${consent}'
            });
            gtag('config', '${GA_ID}', {
              page_path: window.location.pathname,
            });
          ,
        }}
      />
    </>
  );
}



Then to update consent


useEffect(() => {

  // Don't update consent on first render
  if (isFirstRender.current) {
    isFirstRender.current = false;
    return;
  }

  window.gtag("consent", "update", {
    ad_storage: local ? "granted" : "denied",
    ad_user_data: local ? "granted" : "denied",
    ad_personalization: local ? "granted" : "denied",
    analytics_storage: local ? "granted" : "denied",
  });
}, [local]);