Put one script tag at the top of <head> in your React app’s index.html (Vite) or app/layout.tsx (Next.js), then wrap the app in ConsentLayerProvider and place analytics or widgets inside ConsentGate. The tag shows the banner on the first visit, stores the choice, and holds known trackers until the visitor consents, so Google Analytics and ad pixels don’t fire before anyone clicks.
How cookie consent works in a React app
A React app needs consent handled in two places, because some scripts load from the HTML file and others load from components after React renders. The banner tag covers the first group, because it sits in the document head, runs before any other script, and decides what may run once the visitor has made a choice.
The React SDK covers the second group: a provider at the root of the component tree reads the stored choice, and a gate component around each tracker renders its children only when the visitor has accepted the matching category, such as statistics or marketing.
Both layers read the same decision, so a visitor who declines marketing in the banner never sees the marketing component mount. Scripts that stay in the HTML file get a changed type attribute and a category attribute, which keeps the browser from running them until consent for that category exists.
What you need before you start
You need a React app built with Vite or the Next.js App Router, npm, and a ConsentLayer account. Claude Code can run setup_site, write the tag into <head>, deploy, and run check_installation in one session.
Write down every third-party script the app loads before you begin, for example GA4, a Meta pixel, and a chat widget, and note whether each one lives in the HTML file or in a component. That note decides whether a script gets a gate in Step 4 or a tag change in Step 5.
Older tutorials assume a Create React App project with public/index.html. In a Vite project, index.html sits at the project root, next to vite.config.ts. A Next.js App Router project has no HTML file at all, so the tag goes in app/layout.tsx.
Add cookie consent to your React app, step by step
Step 1. Create the site and get your script tag
With an agent, connect ConsentLayer first (the guide to connect ConsentLayer to your coding agent has the details for other tools):
claude mcp add consentlayer --transport http https://api.consentlayer.com/mcp
Then give it one prompt: “Add the ConsentLayer cookie banner to my Vite React app.” Your agent calls setup_site, which creates the site, scans it, configures the consent categories, and returns the install snippet with a real project key, all in one tool call. You sign in through your browser the first time you connect.
Without an agent, sign in at app.consentlayer.com, add the site with its domain, and copy the project key from the install screen.
Expected result: a script tag that contains your real project key.
Step 2. Paste the banner tag into the head
The tag looks like this, with your key in the path:
<script src="https://b.consentlayer.com/PROJECT_KEY/banner.js"></script>
For Vite, open index.html at the project root and paste the tag as the first script inside <head>, exactly as the dashboard or your agent gave it. For Next.js, write it as a plain <script> element inside <head> in app/layout.tsx:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<script src="https://b.consentlayer.com/PROJECT_KEY/banner.js"></script>
</head>
<body>{children}</body>
</html>
);
}
Expected result: the banner shows on the first visit.
Step 3. Install the SDK and wrap the app in ConsentLayerProvider
npm install @consentlayer/sdk
For React 18 and newer, the SDK gives you ConsentLayerProvider, ConsentGate, useConsent, and useCategoryConsent. The provider reads the choice the banner stored, so the app and the banner follow one decision. In a Vite app, wrap the root in main.tsx:
import { ConsentLayerProvider } from '@consentlayer/sdk/react';
createRoot(document.getElementById('root')!).render(
<ConsentLayerProvider><App /></ConsentLayerProvider>
);
Next.js needs a client component, because the layout is a server component. Create components/consent-provider.tsx:
'use client';
import { ConsentLayerProvider } from '@consentlayer/sdk/react';
export function ConsentProvider({ children }: { children: React.ReactNode }) {
return <ConsentLayerProvider>{children}</ConsentLayerProvider>;
}
Then wrap {children} inside <body> of app/layout.tsx with <ConsentProvider>.
Expected result: the hooks and gates work in any component inside the app.
Step 4. Gate analytics and widgets with ConsentGate
Put each tracker that lives in a component inside a gate with its category:
<ConsentGate category="statistics"><AnalyticsScript /></ConsentGate>
<ConsentGate category="marketing" fallback={<p>Enable marketing cookies to see this.</p>}>
<VideoEmbed />
</ConsentGate>
In Next.js, load GA4 with next/script inside the gate:
'use client';
import Script from 'next/script';
import { ConsentGate } from '@consentlayer/sdk/react';
export function Analytics() {
return (
<ConsentGate category="statistics">
<Script src="https://www.googletagmanager.com/gtag/js?id=G-MEASUREMENT" strategy="afterInteractive" />
</ConsentGate>
);
}
I prefer a gate over a tag change for any script you control in React, because the gate keeps the consent rule next to the code that loads the script.
Step 5. Tag static scripts in index.html
Some scripts stay in index.html, for example a heatmap snippet a client pasted years ago. Change its type to text/plain and add data-cl-category with statistics or marketing, and it runs only after consent for that category. Before:
<script src="/js/heatmap.js"></script>
After:
<script type="text/plain" data-cl-category="statistics" src="/js/heatmap.js"></script>
Expected result: the script runs only after the visitor accepts that category.
Step 6. Add a “Manage cookies” button
Visitors need a way back to their choice after the banner closes. Add a client component to the footer:
'use client';
import { useConsent } from '@consentlayer/sdk/react';
export function ManageCookies() {
const { showPreferences } = useConsent();
return <button onClick={showPreferences}>Manage cookies</button>;
}
Expected result: the button opens the preferences panel, where the visitor can change the choice.
Check that it works
Run the browser check on the deployed site, not on the dev server. Open it in a private window, open DevTools, select the Network tab, and type collect in the filter box so only GA4 hits show. Before you click anything, the list must stay empty. After you accept statistics, a request to google-analytics.com/g/collect appears.
A failed check looks different: the collect request is already in the list while the banner is still on screen, and the same goes for a facebook.com/tr request from a Meta pixel.
Then confirm the install with your agent, using the prompt “Check the ConsentLayer installation on my live site”. Your agent runs check_installation, which fetches the live homepage and confirms the tag is in <head> without defer or async. To compare your agent’s replies, follow the full agent setup walkthrough.
Without an agent, view the page source of the live homepage and search for banner.js. The tag must sit inside <head> with no defer and no async attribute.
Common problems and fixes
Google Analytics fires before anyone clicks
Take a Next.js client site where a developer moved the banner into a component as <Script strategy="beforeInteractive">, or added it from a useEffect. The banner still appears, so the client thinks the job is done, but GA4 has sent the page view before the tag even loaded. That site is installed, yet it isn’t blocking anything.
Your fix starts from one rule: the tag must load synchronously, with no defer and no async, before other scripts. A useEffect runs only after React has rendered, and next/script applies its own loading rules, so neither gives you a plain synchronous tag. Move it back to a plain <script> element as the first script in <head>, then repeat the Network check in a private window.
A script you added still runs before consent
The banner holds the trackers it knows, while a script it doesn’t recognize goes to a review queue, which shows you which scripts still need a category. Run this check:
- Open the site’s review queue in the ConsentLayer dashboard.
- Find the script by its domain, for example the chat widget’s CDN.
- Approve it with a category and a blocking rule, or dismiss it if it sets no cookies.
- Reload the site in a new private window and confirm in the Network tab that the request waits for consent.
To learn how the scanner fills that queue, read about script blocking and the scanner.
The banner does not show again after you accept
Your test browser keeps the choice for 365 days by default, so after the first click you won’t see the banner again on that browser. This is the call I get most often after a handover, when a client writes “the cookie banner disappeared” because they accepted it once on their own laptop.
To see the first visit again, use one of these:
- Open a new private window, which starts with no stored choice.
- Clear the site’s cookies and site data in DevTools, under Application.
- Use a second browser profile that you keep only for testing.
When the react-cookie-consent package is enough
Many people who search this phrase mean the npm package called react-cookie-consent. It describes itself as “A small, simple and customizable cookie consent bar for use in React applications.” You install it with npm install react-cookie-consent or yarn add react-cookie-consent, and it ships under the MIT license.
Its onAccept callback runs after the visitor clicks the accept button, and onDecline runs after the decline button, so loading or holding each script is code you write inside those callbacks. That keeps the package small, which is a fair trade on a simple site.
The package fits a site with no third-party trackers and one language, because there’s nothing to hold and no translations to manage. For client sites that load analytics, pixels, and widgets, I’d pick a banner that holds trackers and records every decision, because otherwise you’d write and maintain the blocking code for each script on each site.
React cookie consent FAQ
Does a React cookie banner work with Google Consent Mode v2?
Yes, because ConsentLayer maps its categories to Consent Mode signals: Essential sets functionality and security storage, Statistics sets analytics_storage, and Marketing sets ad_storage, ad_user_data, and ad_personalization. A visitor who accepts only statistics grants analytics storage while the three ad signals stay denied.
What does it cost to add ConsentLayer to one React app?
The Free plan is $0 forever with no credit card, and it covers 1 site, 10,000 pageviews per month, 1 team member, 1 banner language, and a 30-day consent log. On Studio and Agency, one pageview allowance covers every site in the organization. Compare each tier under plans and pageview limits.
Where does the react-cookie-consent package store the visitor’s choice?
In a cookie named by the cookieName prop, which defaults to “CookieConsent”. The expires prop sets how many days it lasts, with a default of 365.
How do you read or reset the choice with react-cookie-consent?
Call getCookieConsentValue(cookieName) to read the stored value, and call resetCookieConsentValue to remove the cookie on the client side. Pass the same name you gave the cookieName prop when you changed it from the default.
Set up the next site from this one
Once one React app is configured, the next site doesn’t need a fresh setup. ConsentLayer exports a site’s full configuration as JSON, including design, behavior, categories, services, cookies, translations, and geo groups, and you import that file into another site.
- Export the configuration of the finished site from its settings.
- Create the new client site and import the JSON file into it.
- Paste the new site’s own tag into its
<head>, then repeat Steps 3 to 6 in that codebase.
To confirm the import worked, open the new site’s categories and services and compare them with the original, then run the same private-window Network check. That gives your roster one tested setup for cookie consent across client sites.
Follow the Vite + React guide to put the banner and ConsentGate on your app.
