To add cookie consent to a React app, load the consent script in the <head> of index.html, before any analytics or marketing tag, and start every tracker only after the visitor accepts its category. A banner component alone is not enough: in a single-page app, a tag that runs when the bundle loads has set its cookies before the banner even renders. The same rule holds for Next.js, with the script in the root layout instead.
Why cookie consent works differently in React
Traditional websites reload the page on every navigation. Each reload gives a cookie management script a fresh chance to intercept tracking before it fires. React applications do not work that way.
A React app loads once. After the initial page load, JavaScript handles every route transition, component mount and state update without a full browser refresh. If your analytics snippet sits in index.html or starts inside a useEffect hook without a consent check, cookies like _ga, _fbp and _gcl_au can be set the moment the bundle executes. Under Article 5(3) of the ePrivacy Directive, storing non-essential cookies before consent is not allowed anywhere in the EU and EEA.
This is not a theoretical risk. On 1 September 2025 the CNIL fined SHEIN 150 million euros, in part because advertising cookies were set as soon as visitors arrived, before they had made any choice on the banner.
Common cookies in React applications
Before adding a consent banner, find out which cookies your React app actually sets. Many come from third-party scripts bundled through npm or loaded with <script> tags in your index.html.
| Cookie | Source | Category | Consent needed? |
|---|---|---|---|
_ga, _ga_* | Google Analytics 4 | Analytics | Yes |
_fbp | Meta Pixel | Marketing | Yes |
_gcl_au | Google Ads | Marketing | Yes |
li_sugr | LinkedIn Insight Tag | Marketing | Yes |
_hjSessionUser_* | Hotjar | Analytics | Yes |
PHPSESSID or another session ID | Your back-end API | Strictly necessary | No |
Running a cookie scan against your deployed React app is the fastest way to build a complete inventory. A scanner loads the app in a real browser, so it sees cookies that third-party scripts set and that your source code does not show.
Two ways to add a React cookie banner
There are two main ways to add cookie consent to a React project. Each has trade-offs.
Approach 1: a script tag in index.html
The most reliable method places the consent script in the <head> of index.html. In a Vite project that file sits at the project root. In an older Create React App project it is public/index.html: the React team deprecated Create React App for new apps on 14 February 2025 and now points new projects to a framework or a build tool such as Vite.
<head>
<script src="https://cdn.kukie.io/s/YOUR-SITE-KEY/c.js" async></script>
<!-- analytics and marketing tags after this line -->
</head>
Because the script runs outside the React component tree, its blocking does not wait for React to mount. That avoids the race in which a component mounts and fires tracking before a consent provider has read the stored choice.
Approach 2: a React component wrapper
Some developers prefer consent as a React component, usually a context provider that wraps the application. Child components then read consent state from context before starting any tracking.
This feels more natural to React developers, but it brings a timing problem. The provider must mount and read stored preferences before any tracking component runs. If your analytics component sits in a lazy-loaded route and mounts before the consent context resolves, tracking fires too early. A script tag in index.html avoids this because it does not depend on React at all, and a context can still read its decision, as shown below.
What the react-cookie-consent package does, and what it leaves to you
Many searches for React cookie consent end at the react-cookie-consent npm package. Its README describes it as a small, customisable consent bar. It renders the bar, stores the answer in a cookie named CookieConsent for 365 days by default, and calls your onAccept or onDecline function.
Three things stay with you. The decline button is hidden unless you set enableDeclineButton, and a banner without an equal way to refuse does not give valid consent in the EU. The package does not block any script, so every tracker still needs your own gate. And it keeps no record of who consented, which you need if a regulator asks for proof. It also offers accept-on-scroll, about which the README itself tells you to consult your legislation. Scrolling is not a clear affirmative act under the GDPR.
The package suits a single-language app with one or two trackers you gate yourself. For per-category choices, geo-specific rules, Consent Mode and a consent log, you need a consent platform.
Blocking scripts until consent is given
The core requirement is plain: no analytics or marketing cookies may be set until the visitor grants consent. In a React app, this means every third-party script sits behind a consent check.
Using the type attribute for script blocking
A common technique changes the type attribute of a tracking script from text/javascript to text/plain. The browser does not run a script with an unknown type. With Kukie.io, you add data-cc-category with the category slug, and the banner runs the script once that category is accepted:
<script type="text/plain" data-cc-category="analytics"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
This works well for scripts in index.html. For scripts that React components load, you need a consent value inside the app.
Conditional script loading in components
Kukie.io dispatches a cc:consent-updated event on document whenever consent is applied, including when a returning visitor's stored choice is restored at page load. Because that restore can fire before React mounts, catch the latest event with a small inline script in index.html, placed before the banner script:
<script>
document.addEventListener('cc:consent-updated', function (e) {
window.__consentDetail = e.detail;
});
</script>
A hook can then read that value on mount and follow later changes:
function useConsent(category) {
const read = () => (window.__consentDetail?.categories ?? []).includes(category);
const [granted, setGranted] = useState(read);
useEffect(() => {
const onUpdate = (e) => setGranted(e.detail.categories.includes(category));
document.addEventListener('cc:consent-updated', onUpdate);
return () => document.removeEventListener('cc:consent-updated', onUpdate);
}, [category]);
return granted;
}
const hasAnalyticsConsent = useConsent('analytics');
useEffect(() => {
if (!hasAnalyticsConsent) return;
const script = document.createElement('script');
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX';
document.head.appendChild(script);
}, [hasAnalyticsConsent]);
The value comes from your consent management solution's API or callback, not from a cookie you parse by hand. The consent tool handles storage, expiry and restores for you.
Handling route changes
React Router does not trigger full page reloads, so the stored choice survives every navigation. What a new route can bring is a new script: a lazy-loaded page may insert a marketing tag that was not present on the first page. Your script-blocking strategy must cover scripts inserted after boot, not only those present at page load. Kukie.io's auto-block watches for known trackers that are added later, as well as those in the original HTML.
Google Consent Mode v2 in React
For traffic from the EEA and the UK, Google uses Consent Mode v2 signals to decide how Google Ads and Google Analytics may use the data. This applies whether your front end is built with React, Vue or plain HTML.
Consent Mode works by setting default consent states (usually denied) before any Google tag sends data, then updating them when the visitor makes a choice. Google's developer guide says the default command must run before any command that sends measurement data. In a React app, put it in the <head> of index.html, before gtag.js or Google Tag Manager loads.
The four parameters are ad_storage, analytics_storage, ad_user_data and ad_personalization. Keep Google's US spelling in code: ad_personalisation is not a parameter, and a default set with it leaves ad personalisation unset. A consent platform sends a consent update command to gtag when the visitor accepts or rejects categories, and Kukie.io's Consent Mode integration does this for you once it is switched on.
Consent state persistence and rehydration
When a visitor returns to your React app, their earlier choice must be remembered. Most consent platforms store it in a first-party cookie or localStorage. Kukie.io uses a first-party cookie named _cc_consent.
On the next visit, the consent script reads the stored preference and applies it before any gated tracking starts. This is another reason the script tag approach works better than a component: the script starts loading while the browser parses the page, while a component only runs after React boots, renders the tree and mounts the provider.
If your React app uses server-side rendering through a framework like Next.js, the consent cookie reaches the server with each request. That lets you make rendering decisions based on consent before sending HTML to the browser.
Testing your React cookie banner
A banner that displays correctly is not necessarily a banner that blocks correctly. You need to check that no tracking cookies appear before consent is given.
Open Chrome DevTools, go to the Application tab and clear all cookies and site data. Reload your React app. Before touching the banner, check the cookies list. If _ga or _fbp already appear, your implementation has a blocking gap. The cookie banner testing guide covers a full verification process.
Also test route transitions. Click through several pages without granting consent, then check the cookies again. SPA navigation should not create new tracking cookies if consent was never given.
Regular scans catch regressions. A new npm package or a colleague's pull request can add a tracking script that skips your consent logic, and a scheduled scan flags it before a regulator does.
React compared with Next.js and Gatsby
Plain React built with Vite, or with the legacy Create React App, is a client-side SPA. The whole application runs in the browser, and the script tag in index.html covers consent reliably.
Next.js adds server-side rendering and static generation. Consent scripts go in the root layout (App Router) or a custom _document file (Pages Router), or through the Next.js Script component with the beforeInteractive strategy. Gatsby uses gatsby-ssr.js to inject scripts during the build. Each framework has its own timing model, so a plain React tutorial does not always transfer.
If your project uses Vue.js or Angular instead, the SPA consent challenges are similar, but the implementation details differ.
Questions about consent in React apps
Is a cookie banner needed if a React app does not use Google Analytics?
Yes, if your app sets any non-essential cookies. Check for marketing pixels, session replay tools, A/B testing scripts and embedded content like YouTube iframes. Any of these can set cookies that need consent under the GDPR and the ePrivacy Directive.
Can a React npm package replace a full cookie consent platform?
Lightweight packages such as react-cookie-consent display a banner and store the answer, but they do not block scripts, log consent or apply different rules by region. A consent platform covers those, which matters if your app serves visitors in several countries.
Does React Router navigation reset cookie consent?
No. Client-side navigation keeps the stored choice, so the banner does not ask again on each route. What blocking must still handle is any script a new route inserts, such as a tag in a lazy-loaded page.
Where should the cookie consent script go in a React project?
In the head of index.html, before any analytics or marketing scripts. In a Vite project that file is at the project root, and in a legacy Create React App project it is public/index.html.
Is Google Consent Mode v2 required for React apps?
If your React app uses Google Ads or Google Analytics with visitors in the EEA or the UK, Google expects Consent Mode v2 signals whatever the front-end framework. A Google-certified consent platform is required only for publishers that show Google ads through AdSense, Ad Manager or AdMob.
How are cookies from third-party npm packages blocked?
Most npm packages that set cookies do so by loading external scripts or sending network requests. Start them inside a useEffect hook that runs only when the relevant consent category is granted.
Check what your React app sets before the first click
The quickest test of a React consent setup is the first page load with no choice made. The Cookie scanner opens your app in a real browser and lists every cookie it finds by name and category, so you can see whether anything slips past the banner.
