All Thoughts
AnalyticsAnalytics

Google Analytics in Next.js 14: GDPR-Compliant Consent Banner with Cookies

Ready to add Google Analytics to your Next.js 14 app without feeling like a creepy data stalker? Let’s whip up a GDPR-compliant consent banner faster than you can say “cookie monster”! 🍪

Why Bother with Consent?

Before we dive in, let’s address the elephant in the room: GDPR compliance. It’s not just about avoiding hefty fines; it’s about respecting your users’ privacy. Plus, it’s a great excuse to add a fancy banner to your site. Win-win!

What You’ll Need

  • Next.js 14 (because you’re living on the edge)
  • TailwindCSS (for that sweet, responsive design)
  • A Google Analytics 4 account (time to graduate from GA3, folks)

Got all that? Great! Let’s cook up some code.

Step 1: Setting Up Google Analytics

First, let’s get cozy with Google Analytics. Install these packages:

npm install client-only @types/gtag.js --save-dev

Now, grab your GA4 Measurement ID (it looks like G-XXXXXXXXXX) from your Google Analytics dashboard. You’ll need it soon.

Step 2: The Google Analytics Component

Create a new file components/GoogleAnalytics.tsx:

1'use client';
2
3import Script from 'next/script';
4import { usePathname, useSearchParams } from 'next/navigation';
5import { useEffect } from 'react';
6
7const pageview = (GA_MEASUREMENT_ID: string, url: string) => {
8  window.gtag('config', GA_MEASUREMENT_ID, { page_path: url });
9};
10
11export default function GoogleAnalytics({ GA_MEASUREMENT_ID }: { GA_MEASUREMENT_ID: string }) {
12  const pathname = usePathname();
13  const searchParams = useSearchParams();
14
15  useEffect(() => {
16    const url = pathname + searchParams.toString();
17    pageview(GA_MEASUREMENT_ID, url);
18  }, [pathname, searchParams, GA_MEASUREMENT_ID]);
19
20  return (
21    <>
22      <Script
23        strategy="afterInteractive"
24        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
25      />
26      <Script
27        id="google-analytics"
28        strategy="afterInteractive"
29        dangerouslySetInnerHTML={{
30          __html: `
31            window.dataLayer = window.dataLayer || [];
32            function gtag(){dataLayer.push(arguments);}
33            gtag('js', new Date());
34
35            gtag('consent', 'default', {
36              'analytics_storage': 'denied'
37            });
38            
39            gtag('config', '${GA_MEASUREMENT_ID}', {
40              page_path: window.location.pathname,
41            });
42          `,
43        }}
44      />
45    </>
46  );
47}

This component loads GA scripts and sets default consent to “denied” because we’re good privacy citizens.

Step 3: The Cookie Consent Banner

Now for the star of the show, our cookie consent banner. Create components/CookieBanner.tsx:

1'use client';
2
3import { useState, useEffect } from 'react';
4import Link from 'next/link';
5import { Cookie } from 'lucide-react';
6
7const getLocalStorage = (key: string, defaultValue: any) => {
8  const stickyValue = localStorage.getItem(key);
9  return stickyValue !== null && stickyValue !== 'undefined'
10    ? JSON.parse(stickyValue)
11    : defaultValue;
12};
13
14const setLocalStorage = (key: string, value: any) => {
15  localStorage.setItem(key, JSON.stringify(value));
16};
17
18export default function CookieBanner() {
19  const [cookieConsent, setCookieConsent] = useState<boolean | undefined>();
20  const [show, setShow] = useState<boolean>(false);
21
22  useEffect(() => {
23    const storedCookieConsent = getLocalStorage('cookie_consent', null);
24    setCookieConsent(storedCookieConsent);
25
26    const timer = setTimeout(() => setShow(true), 3000);
27    return () => clearTimeout(timer);
28  }, []);
29
30  useEffect(() => {
31    const newValue = cookieConsent ? 'granted' : 'denied';
32    window.gtag('consent', 'update', {
33      analytics_storage: newValue,
34    });
35    setLocalStorage('cookie_consent', cookieConsent);
36  }, [cookieConsent]);
37
38  if (!show || cookieConsent !== undefined) return null;
39
40  return (
41    <div className="fixed bottom-0 left-2 right-2 mx-auto mb-2 flex max-w-max flex-col items-center justify-between gap-4 rounded-xl border border-neutral-700 bg-neutral-800 px-3 py-3 shadow sm:max-w-sm sm:flex-row sm:right-auto lg:max-w-lg lg:gap-5 md:px-4 lg:py-4">
42      <div className="flex flex-col items-center gap-2 lg:flex-row">
43        <Cookie className="mt-5 text-[#f1ff81] lg:mt-0" />
44        <div className="w-fit text-center font-mono text-sm text-white lg:text-left">
45          <Link href="/info/cookies">
46            <p>
47              We use <span className="font-bold text-[#f1ff81]">cookies</span> to
48              analyze site traffic and personalize content.
49            </p>
50          </Link>
51        </div>
52      </div>
53      <div className="mt-3 flex gap-2 lg:mt-0 lg:flex-1 lg:text-sm">
54        <button
55          className="rounded-full border border-neutral-700 px-5 py-2 text-gray-300 tracking-tight"
56          onClick={() => setCookieConsent(false)}
57        >
58          Decline
59        </button>
60        <button
61          className="rounded-full bg-white px-5 py-2 font-medium text-neutral-700 tracking-tight"
62          onClick={() => setCookieConsent(true)}
63        >
64          Allow
65        </button>
66      </div>
67    </div>
68  );
69}

This banner is like a polite butler, asking for permission before serving up those delicious analytics cookies.

Step 4: Putting It All Together

Update your app/layout.tsx to include both components:

1import { Suspense } from 'react';
2import GoogleAnalytics from '@/components/GoogleAnalytics';
3import CookieBanner from '@/components/CookieBanner';
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6  return (
7    <html lang="en">
8      <head>
9        <Suspense>
10          <GoogleAnalytics GA_MEASUREMENT_ID="G-YOUR_MEASUREMENT_ID" />
11        </Suspense>
12      </head>
13      <body>
14        {children}
15        <Suspense>
16          <CookieBanner />
17        </Suspense>
18      </body>
19    </html>
20  );
21}

The Grand Finale

And there you have it! You’ve just set up a GDPR-compliant Google Analytics setup with a snazzy consent banner in your Next.js 14 app. Here’s what we accomplished:

  1. Set up Google Analytics with consent mode
  2. Created a responsive, GDPR-compliant cookie banner
  3. Implemented user preference storage
  4. Integrated everything into your Next.js app

Now you can track your users guilt-free! Remember, with great power comes great responsibility. Use your newfound analytics wisely, and may your bounce rates be ever in your favor! 📊🚀