cookie consent for Next.js

Cookie consent for Next.js,
in one command

Run npx cookiecrumbs init in your Next.js repo. It links the site, writes cookiecrumbs.config.ts and drops the tag in with strategy="beforeInteractive", so consent defaults land before hydration and before any tag manager.

What changes in a Next.js page head once the runtime is first: nothing non-essential runs until its category is granted.

the rule

What the law asks of a Next.js site

A Next.js app has no consent layer. next/script loads GA4 or the Meta pixel the moment the page hydrates, and every visitor from the EU is tracked before they have said anything.

The rule is the same everywhere in the EU and the UK: nothing beyond what is strictly necessary may be stored on or read from a visitor’s device before they have agreed. That is Article 5(3) of the ePrivacy Directive, and the agreement has to meet the GDPR’s definition of consent: freely given, specific, informed and unambiguous, with a refusal that is as easy as the acceptance. The UK keeps the same rule in PECR, with narrow exemptions for low-risk statistics since 5 February 2026. California and the other US state laws work the other way round: no opt-in, but an opt-out link and support for the Global Privacy Control signal.

Three things follow for a website. Analytics, advertising and social scripts have to wait for a choice. The choice has to be recorded in a way you can show later, because Article 7(1) puts the burden of proof on you. And withdrawing has to be as easy as agreeing, which means a control that reopens the banner on every page. A banner that shows two buttons and loads Google Analytics anyway fails the first test, and that is the failure regulators have been fining, from the CNIL’s decisions against Google and SHEIN to the Dutch and Belgian authorities’ sweeps.

how it works

How consent works in a Next.js app

Next.js gives you exactly one good place for a consent runtime: a next/script tag with strategy="beforeInteractive". Scripts with that strategy are injected into the initial HTML and run before any hydration, which is the only moment early enough. afterInteractive (the default) runs after hydration, by which time a tag manager added with the same strategy may already have fired. lazyOnload is worse still.

In the App Router the tag goes into app/layout.tsx, which wraps every route. In the Pages Router it goes into pages/_document.tsx. The CLI detects which one you use and prints the right snippet; @cookiecrumbs/next wraps the tag as a component and adds server helpers, so a Server Component or a route handler can read the consent cookie and decide what to render before the client runs.

From there, gating is a matter of category. A third-party tag becomes type="text/plain" with data-cc-category; a React subtree that loads a tracker goes inside ConsentGate. The runtime releases both when the category is granted, in document order, so a GTM container that waits for analytics runs exactly as it would have, just later. Everything the scan found on your production domain is compiled into a block map at publish, so untagged scripts whose host is known are neutralised in place as well.

set-up

Set it up in Next.js

Five steps. The CLI does most of them, and every one is also a click in the dashboard.

  1. Init from the repo

    Detects Next.js, links or creates the site in your workspace, writes the config and the text files, and prints the snippet for your router.

    npx cookiecrumbs login
    npx cookiecrumbs init
  2. Put the tag first

    App Router: in app/layout.tsx. Pages Router: in pages/_document.tsx. beforeInteractive keeps it ahead of hydration and of any tag manager.

    // app/layout.tsx
    import Script from 'next/script';
    
    <Script src="https://app.cookiecrumbs.eu/runtime/cc.js"
      strategy="beforeInteractive"
      data-cc-site="pk_live_…" data-cc-env="production" />
  3. Gate what needs consent

    Give consent-bound tags type="text/plain" and a category, or wrap components in ConsentGate from @cookiecrumbs/next. The runtime releases them when the category is granted.

    import { ConsentGate } from '@cookiecrumbs/next';
    
    <ConsentGate category="analytics" fallback={null}>
      <Analytics />
    </ConsentGate>
  4. Scan, sort, publish

    The first scan finds what the app really loads. Sort each vendor once, set your legal links, publish, and the consent log starts recording.

    npx cookiecrumbs scan --wait
    npx cookiecrumbs push --env production
  5. Gate the deploy

    In CI, fail the build when a new tracker appears unclassified. SARIF output lands in code scanning.

    npx cookiecrumbs scan --wait --fail-on-unknown --fail-on-preconsent --sarif results.sarif

watch out

Mistakes we see in Next.js apps

Each one shows up in the first scan as a tracker that fired before consent.

  • Loading the runtime with afterInteractiveBy then the page has hydrated and a tag manager with the same strategy may already have run. Only beforeInteractive is early enough.
  • A banner component that only hides itselfSetting a cookie and hiding the box does nothing to the scripts that already ran. Blocking has to happen at the script tag, not in the UI.
  • Vercel Analytics and Speed Insights as “necessary”They are analytics. File them under analytics and gate them; the free tier still counts.
  • Middleware that sets marketing cookiesAn A/B test or a referrer cookie set in middleware runs before any consent exists. Keep those strictly necessary or move them behind consent.
  • Forgetting the preview deploymentPreview URLs get the preview key and their own environment, so testing never touches real visitors.

before you ship

The compliance checklist

Eight lines. The dashboard ticks the last two from real data.

  • The tag is the first script in the headAnything above it can set a cookie the runtime cannot take back.
  • Every analytics, ads and social script waits for its categoryEither type="text/plain" with a category, or the host is in the block map from the scan.
  • Reject all is on the first layerSame size, same weight as Accept all. No “X” that counts as consent, no pre-ticked boxes.
  • Consent Mode defaults are denied before Google loadsCheck the seven signals in Tag Assistant with consent denied.
  • The imprint and privacy policy links are setPublishing is refused until both are there; the banner footer links to them.
  • A withdraw control is reachable on every pageThe persistent control or a “Manage cookies” link in your footer.
  • The first full scan ran and every vendor is filedThe verdict on Home says what fired before consent, dated.
  • The install check passedIt loads your page like a visitor and confirms the banner ran before any tracker.

what you get

What you get on the free plan

Two domains, 100 subpages scanned a month per domain, every regulation, the CLI, the SDKs and the MCP server.

A scanner that reads the rendered page

Every page, in each consent state, in a real browser. Cookies, pixels, iframes and storage keys, with the page and script that set them. Re-scanned on a schedule.

The cookie declaration writes itself from the result.

A banner that blocks first

Tags stay inert until their category is granted. Reject all is as visible as Accept all. Seven layouts, 25 languages, Consent Mode v2, and the right rule per region.

21 kB, one file, no third-party requests of its own.

Proof you can hand over

Each choice is stored with a timestamp, the banner version and a hash of the texts shown, chained to the record before it. Exports are signed. Hosted in Frankfurt.

Article 7(1), on the free plan.

free check

Check what your site loads right now

The homepage HTML, matched against the tracker database. No account, nothing stored. The full scan in the dashboard does the same for every page, in a real browser.

questions

Straight answers

Does it work with the App Router and the Pages Router?
Yes. The tag goes in app/layout.tsx with next/script and strategy="beforeInteractive" for the App Router, or in pages/_document.tsx for the Pages Router. The CLI prints the right snippet for your project.
Does the banner slow down my Core Web Vitals?
The runtime is 21 kB gzipped, one file, loaded async, and the bar and ribbon layouts reserve their space before paint, so it adds no layout shift. On Vercel it is served from our own domain with long cache lifetimes.
Can I keep the config in git?
Yes. cookiecrumbs.config.ts holds design, texts, categories, languages and region rules. cookiecrumbs push deploys it and cookiecrumbs pull brings dashboard edits back into the repo, so a banner change is a pull request.
Can a Server Component read the consent?
Yes. @cookiecrumbs/next exposes the consent cookie to server helpers, so you can render a placeholder instead of an embed before the client ever runs.
What about Google Tag Manager in Next.js?
Keep it. The runtime sets Consent Mode v2 defaults before GTM loads and updates them on consent; the GTM template adds the triggers. Or give the GTM tag a category and let it wait entirely.
Does the free plan cover a Next.js side project?
Two domains, 100 subpages scanned a month per domain, all regulations, the SDK, the CLI and the MCP server. Consent records are kept 12 months on Free and 60 months on every paid plan.

next step

Ship the banner with the next deploy

Add the site, run npx cookiecrumbs init in your Next.js repo, and the consent log starts recording in the EU.