Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
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”! 🍪
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!
Got all that? Great! Let’s cook up some code.
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.
Create a new file components/GoogleAnalytics.tsx
:
'use client';
import Script from 'next/script';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
const pageview = (GA_MEASUREMENT_ID: string, url: string) => {
window.gtag('config', GA_MEASUREMENT_ID, { page_path: url });
};
export default function GoogleAnalytics({ GA_MEASUREMENT_ID }: { GA_MEASUREMENT_ID: string }) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
const url = pathname + searchParams.toString();
pageview(GA_MEASUREMENT_ID, url);
}, [pathname, searchParams, GA_MEASUREMENT_ID]);
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
/>
<Script
id="google-analytics"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('consent', 'default', {
'analytics_storage': 'denied'
});
gtag('config', '${GA_MEASUREMENT_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</>
);
}
This component loads GA scripts and sets default consent to “denied” because we’re good privacy citizens.
Now for the star of the show, our cookie consent banner. Create components/CookieBanner.tsx
:
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { Cookie } from 'lucide-react';
const getLocalStorage = (key: string, defaultValue: any) => {
const stickyValue = localStorage.getItem(key);
return stickyValue !== null && stickyValue !== 'undefined'
? JSON.parse(stickyValue)
: defaultValue;
};
const setLocalStorage = (key: string, value: any) => {
localStorage.setItem(key, JSON.stringify(value));
};
export default function CookieBanner() {
const [cookieConsent, setCookieConsent] = useState<boolean | undefined>();
const [show, setShow] = useState<boolean>(false);
useEffect(() => {
const storedCookieConsent = getLocalStorage('cookie_consent', null);
setCookieConsent(storedCookieConsent);
const timer = setTimeout(() => setShow(true), 3000);
return () => clearTimeout(timer);
}, []);
useEffect(() => {
const newValue = cookieConsent ? 'granted' : 'denied';
window.gtag('consent', 'update', {
analytics_storage: newValue,
});
setLocalStorage('cookie_consent', cookieConsent);
}, [cookieConsent]);
if (!show || cookieConsent !== undefined) return null;
return (
<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">
<div className="flex flex-col items-center gap-2 lg:flex-row">
<Cookie className="mt-5 text-[#f1ff81] lg:mt-0" />
<div className="w-fit text-center font-mono text-sm text-white lg:text-left">
<Link href="/info/cookies">
<p>
We use <span className="font-bold text-[#f1ff81]">cookies</span> to
analyze site traffic and personalize content.
</p>
</Link>
</div>
</div>
<div className="mt-3 flex gap-2 lg:mt-0 lg:flex-1 lg:text-sm">
<button
className="rounded-full border border-neutral-700 px-5 py-2 text-gray-300 tracking-tight"
onClick={() => setCookieConsent(false)}
>
Decline
</button>
<button
className="rounded-full bg-white px-5 py-2 font-medium text-neutral-700 tracking-tight"
onClick={() => setCookieConsent(true)}
>
Allow
</button>
</div>
</div>
);
}
This banner is like a polite butler, asking for permission before serving up those delicious analytics cookies.
Update your app/layout.tsx
to include both components:
import { Suspense } from 'react';
import GoogleAnalytics from '@/components/GoogleAnalytics';
import CookieBanner from '@/components/CookieBanner';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Suspense>
<GoogleAnalytics GA_MEASUREMENT_ID="G-YOUR_MEASUREMENT_ID" />
</Suspense>
</head>
<body>
{children}
<Suspense>
<CookieBanner />
</Suspense>
</body>
</html>
);
}
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:
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! 📊🚀