Cookie consent for React

In a single-page app the banner goes in index.html, not in a component: it has to run before anything else, and it should survive every re-render. Your components can still react to the visitor's choice through one browser event.

Set it up with your AI agent

Connect the Croombit MCP server to your editor or coding agent and paste this. It creates the site, the cookie categories and the banner, and edits your code.

Using Croombit: create a site for this project with Analytics (ANALYTICS) and Advertising (MARKETING) cookie categories, off by default, publish them and publish the banner. Create a TEST publishable key for localhost:3000. This is a React app built with Vite: add the cmp.js tag first in <head> of index.html, reading the key from VITE_CROOMBIT_KEY, and only initialise our analytics SDK after the croombit:consent event grants ANALYTICS.

The agent works in TEST. Going live takes two steps in the console: verify your domain and swap in a LIVE key — see going live.

Or by hand

1. Add the tag to index.html

First in <head>. Vite replaces %VITE_…% placeholders in index.html with your environment variables at build time; with Create React App, it's public/index.html and %REACT_APP_CROOMBIT_KEY%.

<!-- index.html (Vite) -->
<head>
  <script src="https://croombit.com/cmp.js"
          data-tc-api-key="%VITE_CROOMBIT_KEY%"></script>
  <!-- …everything else after it -->
</head>

2. Hold back trackers

Tags in index.html: change them to type="text/plain" with data-tc-consent. Tags your code inserts at runtime from well-known hosts are held back automatically.

<script type="text/plain" data-tc-consent="analytics" async
        src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script type="text/plain" data-tc-consent="analytics">
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXX');
</script>

3. React to the decision

An analytics or session-replay SDK you install from npm isn't a script tag, so start it only when its category is granted:

import { useEffect, useState } from 'react';

// Fires after every decision — e.g. to start an analytics SDK you load from npm.
export function useConsent() {
  const [categories, setCategories] = useState<Record<string, boolean>>({});
  useEffect(() => {
    const on = (e: Event) => setCategories((e as CustomEvent).detail?.categories ?? {});
    window.addEventListener('croombit:consent', on);
    return () => window.removeEventListener('croombit:consent', on);
  }, []);
  return categories; // { ANALYTICS: true, MARKETING: false, … }
}
<a href="#" data-tc-open-preferences>Cookie settings</a>

Questions

Is there a React component instead?

The banner is framework-agnostic on purpose: one tag, rendered outside React. For a fully custom UI, the JavaScript SDK gives you the purposes, the current state and the call to record a decision.

Does it work with React Router or TanStack Router?

Yes. The decision applies to every route; nothing needs to run on navigation.