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:
- Set up Google Analytics with consent mode
- Created a responsive, GDPR-compliant cookie banner
- Implemented user preference storage
- 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! 📊🚀