cookie consent for React

Cookie consent for React,
one tag and a component

A Vite or CRA app gets the runtime as the first tag in index.html, then @cookiecrumbs/react gives you ConsentGate, CookieDeclaration and ManageCookiesLink for the tree.

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

the rule

What the law asks of a React site

A React single-page app loads its analytics in main.tsx before any UI exists. A banner component added later shows a choice but cannot take back what already ran.

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 React app

A React SPA has one HTML file, and that is where consent has to start. The runtime goes into index.html as the first script in the head, before the Vite or Webpack bundle. It installs its interceptor synchronously, before it even fetches your configuration, so anything the bundle tries to inject afterwards is caught.

Inside the tree you have two tools. ConsentGate renders its children only when a category is granted and shows your fallback otherwise, which is the right way to mount a chat widget, a video embed or an analytics provider. The runtime API on window, wrapped by @cookiecrumbs/react as a hook, tells any component the current state, the regime, the language and the consent id, and gives it open() to reopen the banner from a footer link.

Popular React patterns that trip people up: an analytics provider at the top of the tree that fires on mount, a router that sends page views on every navigation, and a useEffect that injects a pixel. Each of them either goes inside a gate or checks isGranted("analytics") before it does anything. The scan on your deployed site shows which of them still fire before consent, with the script that did it.

set-up

Set it up in React

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

  1. Init from the repo

    Detects a plain React project, links the site, writes the config and prints the snippet.

    npx cookiecrumbs login
    npx cookiecrumbs init
  2. Put the tag first in index.html

    Before your bundle, before everything. The runtime installs its interceptor synchronously.

    <!-- index.html -->
    <head>
      <script src="https://app.cookiecrumbs.eu/runtime/cc.js"
        data-cc-site="pk_live_…"
        data-cc-env="production"></script>
      …
    </head>
  3. Gate consent-bound scripts and components

    A type="text/plain" tag with a category stays inert until granted. In the tree, ConsentGate renders its children only when the category is granted.

    <script type="text/plain" data-cc-category="analytics"
            src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
    
    // in the app
    import { ConsentGate } from '@cookiecrumbs/react';
    <ConsentGate category="marketing">
      <MetaPixel />
    </ConsentGate>
  4. Give people a way back in

    A footer link that reopens the banner is the withdrawal path Article 7(3) expects.

    import { ManageCookiesLink } from '@cookiecrumbs/react';
    <ManageCookiesLink>Manage cookies</ManageCookiesLink>
  5. Scan, sort, publish

    The scan finds what the bundle really loads. Sort once per vendor, publish, done.

    npx cookiecrumbs scan --wait
    npx cookiecrumbs push --env production

watch out

Mistakes we see in React apps

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

  • Injecting the runtime from a useEffectEffects run after first render. By then the bundle has loaded and the analytics provider has fired. The tag belongs in index.html.
  • Treating “session” cookies as necessaryNecessity is about function, not lifetime. A session cookie from a marketing tool is still marketing.
  • Storing consent only in React stateA refresh forgets it and the banner asks again. Let the runtime own the cookie and read from it.
  • One banner for every countryThe US needs opt-out and GPC, the EU needs opt-in. Region rules pick the right one from the visitor’s location.
  • Testing on localhost onlyThe scan and the install check need the deployed URL. Use the preview key on a staging deploy.

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

Do I need an npm package?
No. The script tag is enough for blocking and the banner. @cookiecrumbs/react adds typed components and hooks for React trees.
How do I read the consent state in a component?
The runtime API exposes it on window and @cookiecrumbs/react wraps it as a hook, so a component can render a placeholder until the category is granted.
What about a React app that uses Google Tag Manager?
Give the GTM tag type="text/plain" with a category, or let Consent Mode v2 handle it: the runtime sets all seven signals to denied before GTM loads and updates them on consent.
Does this work with Remix, Gatsby or Vite SSR?
Anything that renders an HTML document works with the script tag as the first element of the head. The React components work in any React tree.
Will it break my Lighthouse score?
The runtime is 21 kB gzipped, async, with no third-party requests of its own. Bar and ribbon layouts reserve space before paint, so no layout shift.
How do I show the cookie list on my privacy page?
Mount CookieDeclaration anywhere. It renders the public cookie list from your last scan, in the visitor’s language.

next step

Ship the banner with the next deploy

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