Google Analytics for Next.js 13: Consent Mode with GDPR Compliant Cookie Banner
Next.js 13 has introduced a new feature called Consent Mode, which allows you to control how Google Analytics uses cookies and processes personal data. This is particularly important for websites that are subject to the General Data Protection Regulation (GDPR) and other privacy laws.
With Consent Mode, you can implement a GDPR compliant cookie banner on your Next.js 13 website to obtain user consent before setting any cookies for Google Analytics. This ensures that you are transparent and respectful of your users’ privacy rights.
To implement Consent Mode with a GDPR compliant cookie banner in Next.js 13, follow these steps:
- Install the next-optimized-google-analytics package using npm or yarn:
npm install next-optimized-google-analytics
- Create a new file called _document.js in the /pages directory of your Next.js 13 project. This file will be used to customize the HTML document for your Next.js application.
import Document, { Html, Head, Main, NextScript } from 'next/document';
import Script from 'next-optimized-google-analytics';
class MyDocument extends Document {
render() {
return (
);
}
}
export default MyDocument;
By implementing Consent Mode with a GDPR compliant cookie banner in Next.js 13, you can ensure that your website is in compliance with privacy regulations and respects the rights of your users. This can help build trust and credibility with your audience while also protecting their privacy.
🔗 For more details, check out the accompanying blog post: gaudion.dev/blog/setup-google-analytics-with-gdpr-compliant-cookie-consent-in-nextjs13
Well done! Fantastic tutorial. I'm curious what percentage of users deny cookies…
Really helpful 🙌
thanks for an amazing blog on remote mdx in next js 😘
Can anyone explain me one thing(could be a silly thing which i am not able to understand)
so here:
const [cookieConsent, setCookieConsent] = useState(false)
we have setted cookieConsent default value to false, and here
${
cookieConsent !== null ? "hidden" : "flex"
} px-3 md:px-4 py-3 justify-between items-center flex-col sm:flex-row gap-4
bg-gray-700 rounded-lg shadow`}
we have put a condition that if the cookieConsent is null then only show the cookie banner but here cookie consent will never be null because the default value is false no?
help me with this anyone please
Great video and really informative! I feel like I understand GA a lot better now!
I had an issue with the consent defaulting to false due to the initial state which is set to false. Therefore my localStorage did not really persist since it was overwritten on page reloads. I think that with this implementation everybody should have the issue but I might be wrong.
@Ryan wouldnt the default useState(false) always trigger the useEffect with cookieConsent == false which leads to overwritting the localStorage?
For anyone facing the same issue I`ll add my fix below 🤖
To fix this I had to default the state to the localStorage if it existed:
const [cookieConsent, setCookieConsent] = useState(() => {
const storedCookieConsent = getLocalStorage("cookie_consent", null);
return storedCookieConsent !== null ? storedCookieConsent : false;
});
I then ran into issues with SSR (maybe because I did not use the app/router, I am not sure about that)
I fixed it by importing the CookieConsent.tsx dynamically in my _app.tsx
import dynamic from "next/dynamic";
const CookieConsentNew = dynamic(
() => import("@/components/CookieConsent"),
{
ssr: false,
}
);
Saved my day thank you for the video, helped alot 🙂
Anyone else always getting cookie_consent false and CookieBanner not re rendering using this code ?
Super helpful!
great job, thanks for the vid!
Thanks 🎉
Great content, it was very helpful! Let me share one piece of knowledge as well.
In your implementation of the Banner, you have 2 use effects, which will cause one extra re-render when changing the consent. I think it's better to avoid the extra effect and have a function to call when user clicks (or when the component mounts). See the code below and let me know if it make sense. 🚀
“`
const [isVisible, setIsVisible] = useState(false);
const setCookieConsent = useCallback((isAllowed: boolean) => {
const newValue = isAllowed ? 'granted' : 'denied';
setLocalStorage('cookie_consent', isAllowed);
window.gtag('consent', 'update', {
analytics_storage: newValue,
});
}, []);
useEffect(() => {
const storedCookieConsent = getLocalStorage('cookie_consent', null);
storedCookieConsent === null && setIsVisible(true);
setCookieConsent(storedCookieConsent);
}, [setCookieConsent]);
const handleChangeConsent = (isAllowed: boolean) => () => {
setCookieConsent(isAllowed);
setIsVisible(false);
};
if (!isVisible) {
return null;
}
“`
There a small little bug with the persistence of the banner.
In CookieBanner I removed the first `useEffect` and moved the `getLocalStorage` to the default value of `useState`
“`
const [cookieConsent, setCookieConsent] = useState(() => getLocalStorage("cookie_consent", null));
“`
hi, i found a bug on the result of static rendering, it's produce id="__next_error__" and failed to generate static page. the cause of this bug is because we use useSearchParams on this goole analytics component <GoogleAnalytics …/>, from next.js docs: "If a route is statically rendered, calling useSearchParams() will cause the tree up to the closest Suspense boundary to be client-side rendered." So we need wrap <GoogleAnalytics …/> component with <Suspense fallback={fallbackComponent}> <GoogleAnalytics …/> </Suspense>
How to get the _ga client id from the cookie in NextJS 13?
hi, sorry, i think this code shoulde useState(
getLocalStorage("cookie_consent", null)
) not useState(false)
const [cookieConsent, setCookieConsent] = useState(false);
useState(false) on this line of code means that the user already decline the cookie and make cookie banner not show
useState(
getLocalStorage("cookie_consent", null)
) make default value is null if user not decide decline or accept cookies
cmiiw, thanks for this tutorials, it's really helpful to me
How can we detect all the cookies (not only GA) and create a consent banner to choose which ones to keep and which ones to delete. Im willing to pay for a course about this.
I want to get information from API such as page view, realtime active, what should I do? Old tutorials use Next/Route but in nextjs 13 useRouter is imported from Next/Navigation
Amazing video. Clear to the point and helpful. Thank you so much!
Thank you! It is exactly what I was looking for!