TL;DR
A Next.js site that loads Google Analytics, the Meta Pixel or any other tracker must collect visitor consent before those scripts run, as required by the GDPR and the ePrivacy rules. Showing a banner is not enough: non-essential scripts must stay blocked until the visitor explicitly agrees, Google Consent Mode v2 signals must start in the denied state, the choice must be respected on every client-side navigation, and you need proof of each consent. This guide covers the GDPR requirements, the Next.js specifics (App Router, Server Components, SPA navigation, SSR) and a step-by-step implementation.
Why cookie compliance matters for a Next.js app
Next.js is a React framework widely used for marketing sites, e-commerce stores, SaaS products and editorial platforms, often deployed on Vercel or Netlify. Its flexibility is also its main compliance risk: developers routinely add Google Analytics 4, Google Tag Manager, the Meta Pixel, Hotjar, Intercom and more, each of which sets cookies or similar trackers that require prior consent.
The most common mistake is loading these scripts in _app.tsx, app/layout.tsx or a shared Layout component with no consent check at all. The pattern is extremely widespread and amounts to a clear GDPR violation: trackers fire on the first page view, before the visitor has had any chance to choose.
The consequences are concrete. European regulators have fined major websites tens of millions of euros over cookie consent, complaints from digital rights organisations such as noyb are frequent, and a banner that does not do what it says damages user trust.
What the GDPR requires for cookies in Next.js
The GDPR, together with the ePrivacy Directive, sets three core obligations for non-essential cookies: inform users clearly, collect their consent before activating trackers, and let them withdraw that consent as easily as they gave it.
Which cookies require consent?
Most third-party tools integrated into a Next.js app require consent:
- Analytics: Google Analytics 4, and Matomo or Plausible when not configured for an exemption
- Advertising and marketing: Meta Pixel, LinkedIn Insight Tag, Google Ads, TikTok Pixel
- User experience: Hotjar, FullStory, LogRocket, Microsoft Clarity
- Support and chat: Intercom, Drift, HubSpot Chat
Cookies that are strictly necessary for the application to work (session, authentication, shopping cart) are exempt. Note that localStorage and sessionStorage follow the same rules as cookies: a tracker that writes to storage needs consent too.
The three core obligations
- Inform: describe which cookies are used, why and by whom, before any of them is set.
- Collect valid consent: freely given, specific, informed and unambiguous. No pre-ticked boxes, no implied consent through scrolling, and a Reject option as easy to use as Accept.
- Respect the choice: do not load rejected scripts, allow withdrawal at any time, and ask again after a reasonable period. Trackers should not live longer than 13 months (see our guide to the 13-month cookie retention rule).
The Next.js specifics
Server Components vs Client Components
With the App Router (Next.js 13 and later), the boundary between Server and Client Components matters for consent:
- Server Components run on the server and have no access to
document.cookieorlocalStorage. They can only learn about consent through the HTTP cookie sent with the request. - Client Components (marked
'use client') run in the browser and can react to consent events. - Middleware and Route Handlers can read the consent cookie to adapt rendering or server-side processing.
Client-side navigation
A Next.js app navigates with history.pushState, without reloading the page. On a classic site, every page load re-runs the banner and re-blocks tracking scripts. In a single-page app, scripts injected after the first page would not be re-checked unless your consent tool hooks into client-side navigation.
The script loading problem
The next/script component offers several loading strategies. With strategy="afterInteractive", a script loads as soon as the page becomes interactive, before any click on the banner. Without a consent mechanism in front of it, GA4 or GTM fires before the banner even appears. This is the single most common violation in Next.js applications.
Two ways to manage consent
Option 1: use a CMP (recommended)
A Consent Management Platform such as FlowConsent handles banner display, consent collection, prior blocking of third-party scripts, Google Consent Mode v2 signals and proof of consent. Translations and regulatory changes are maintained for you.
Option 2: build it yourself
A homemade banner is feasible with a React component and a global Context or store, but the list of things to get right is long:
- blocking every non-essential script, including those injected by GTM or third-party embeds
- sending Consent Mode v2 defaults before any Google tag
- re-checking consent on client-side navigation
- storing proof of consent (timestamp, banner version, choice per category) server-side
- renewing consent and handling withdrawal
- following every regulatory update yourself
Installing FlowConsent in Next.js, step by step
The FlowConsent banner is a single self-contained script, with no npm package to install for the browser side. The exact snippet for your project, with your deployment URL and license code, is generated in Builder → Deployment → Integration, where a Next.js tab is available. The full procedure is in the Next.js and React SPA integration guide. For a React app without Next.js (Vite, Create React App, React Router), follow the React installation guide instead.
Step 1: add the script
- App Router: in
app/layout.tsx, add the FlowConsent script with<Script strategy="beforeInteractive">. This strategy makes Next.js load it before hydration, so prior blocking is in place before any tracker fires. - Pages Router: in
pages/_document.tsx, put the FlowConsent<script>first inside<Head>.
The integration snippet also includes an inline Google Consent Mode stub that registers denied defaults; keep it before the loader. Do not load FlowConsent from a React component or a useEffect: by the time that code runs, other scripts will already have fired.
Step 2: enable SPA Mode
In the Builder, open the Settings tab, turn on SPA Mode and redeploy the banner. FlowConsent then intercepts history.pushState and history.replaceState, listens for popstate, and re-blocks uncovered tracking scripts on every navigation. It also emits a flowconsent:navigation event you can listen to.
Step 3: let FlowConsent manage tracking scripts
Do not load Google Analytics, the Meta Pixel or similar tools with next/script. Declare them in Builder → Services instead, and the banner injects them only once the matching category (analytics, marketing, preferences) is accepted. For embeds such as YouTube iframes, a data-service attribute lets the banner gate them.
The same applies to @next/third-parties (Next.js 14 and later): its GoogleAnalytics component loads GA4 through next/script, outside your consent tool's control. If you want FlowConsent to handle GA4, configure the service in the Builder rather than through this package.
Google Consent Mode v2 in Next.js
Google Consent Mode v2 has been required since March 2024 for advertisers who want to keep using Google Ads audience and measurement features for users in the European Economic Area. It sends four signals to Google: analytics_storage, ad_storage, ad_user_data and ad_personalization.
FlowConsent implements it out of the box, as detailed in the Google Consent Mode guide:
- an inline stub registers
denieddefaults for all four signals before any Google tag loads wait_for_update: 500gives the banner time to apply a returning visitor's stored choice- once the visitor chooses, the banner sends
gtag('consent', 'update', …) - after a refusal, Google tags only send cookieless pings (
gcs=G100), which respect the refusal
Whether you use GTM or gtag.js directly, the rule is the same: the consent defaults must be in place before any Google tag initialises. For the full method, see our Google Consent Mode v2 implementation guide.
Reading consent on the server
Some processing starts server-side: GA4 Measurement Protocol events, CRM sync, attribution logic in a Route Handler, personalised rendering in a Server Component. The banner cannot block these, so your code must check consent itself.
The @flowconsent/server-sdk package reads the first-party cookie set by the banner and returns a typed decision. It has no dependencies and runs on the edge (Vercel, Cloudflare Workers) as well as on Node 18 and later, which makes it usable in Middleware, Route Handlers and Server Components. The decision is denied by default when the cookie is missing, unreadable or stale, and decision.can('analytics') or decision.can('google-analytics') tells you whether a category or a service is allowed.
Reacting to consent in your React code
For logic that lives in Client Components, FlowConsent dispatches a flowconsent:consent event whose detail.preferences object contains the visitor's choice per category. A small hook that subscribes to it lets you:
- initialise your own analytics only when
preferences.analyticsis true - load components that depend on a tracker through
next/dynamiconly after consent - update the UI when a visitor changes their mind
Internationalisation
Next.js handles i18n routing natively, and your banner should speak the same language as the page. In FlowConsent, banner texts are translated in the Translations tab, and the detection method is set in Information. The number of languages per banner depends on the plan: 2 on Free, 5 on Pro, custom on Enterprise.
Performance and Core Web Vitals
Consent management should not hurt your Core Web Vitals:
- reserve
strategy="beforeInteractive"for the consent script only, never for the blocked trackers - blocked third-party scripts use no bandwidth before consent, which lightens the first page load
- check the Lighthouse score before and after installing the banner
Common mistakes in Next.js cookie compliance
Loading GA4 or GTM without a consent check. A next/script tag with afterInteractive runs before any click on the banner. Let the CMP inject trackers instead.
Loading the CMP too late. Injecting the banner from a component or a useEffect means trackers already in the page have fired by the time it starts.
Forgetting client-side navigation. Without SPA Mode, scripts added after the first page are not re-blocked on later routes.
Not blocking the Meta Pixel. It sets advertising cookies and must depend on the marketing category. Our Meta Pixel GDPR guide covers the details.
Pre-ticked boxes or passive consent. A banner with Analytics pre-checked invalidates consent. Every non-essential category must start unchecked.
Confusing stored state with proof. A cookie or localStorage value remembers the choice in one browser, but it is not proof. Proof is a server-side record with a timestamp, banner version and choice per category (see what to store as proof of consent).
Never asking again. Consent should be renewed periodically, and tracker lifetimes must not exceed 13 months.
No way to withdraw. Visitors must be able to reopen their preferences at any time, via a footer link or a floating button.
Testing your implementation
- Open the live site in a private window: the banner appears on the first page.
- In the DevTools Network and Application tabs, check that no tracking request goes out and no non-essential cookie is set before Accept, including after several route changes.
- Use Google Tag Assistant to confirm that Consent Mode starts in
deniedand switches tograntedonly after acceptance. - Run the setup wizard: it checks on your live page that the stub and loader are present, in the right order.
- Finish with a refusal scan from the Compliance tab: it loads your pages in a real browser, refuses consent and lists anything that still fires (compliance verification).
Next.js cookie compliance checklist
- Audit every third-party script (
next/script, GTM, direct imports,@next/third-parties). Our cookie audit guide details the method. - Categorise each cookie: strictly necessary, preferences, analytics, marketing.
- Add the consent script in
app/layout.tsxwithbeforeInteractive, or first in<Head>ofpages/_document.tsx. - Enable SPA Mode and redeploy the banner.
- Declare trackers in the CMP rather than loading them with
next/script. - Check that Consent Mode v2 defaults are
deniedbefore any Google tag. - Gate server-side processing on a consent decision read from the request.
- Offer a Reject button as visible as Accept, with no pre-ticked boxes.
- Store proof of consent and renew it periodically.
- Provide a preferences link on every page.
- Verify in a private window, then with a refusal scan.
Conclusion
GDPR compliance in a Next.js application comes down to one principle: no non-essential script runs before the visitor consents, on the first page and on every client-side navigation. Next.js makes trackers easy to add, so that rule needs a reliable mechanism: a consent script loaded before hydration, SPA Mode, trackers managed by the CMP and consent checked on the server too.
Start by scanning your Next.js site with FlowConsent to see which trackers currently load without consent.