> ## Documentation Index
> Fetch the complete documentation index at: https://docs.katla.app/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> Integrate Katla cookie consent into any React application.

The `@katla.app/sdk/react` entry point provides React components and hooks for managing cookies and consent. Use it with Vite, Create React App, or any client-side React setup.

<Note>
  For Next.js projects, use the [Next.js guide](/sdk-nextjs) instead — it provides a streamlined setup for the App Router.
</Note>

## Prerequisites

* React 18 or later
* `@katla.app/sdk` installed ([see SDK overview](/sdk))

## Setup

Wrap your app with `KatlaProvider`:

```tsx theme={null}
import { KatlaProvider } from '@katla.app/sdk/react';

function App() {
  return (
    <KatlaProvider siteId="your-site-id">
      {/* Your app */}
    </KatlaProvider>
  );
}
```

`KatlaProvider` includes `KatlaGuard` and `ConsentBridge` automatically. To enable Google Consent Mode, pass the `googleConsentMode` prop:

```tsx theme={null}
<KatlaProvider siteId="your-site-id" googleConsentMode>
  {/* Your app */}
</KatlaProvider>
```

| Component           | Purpose                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| `KatlaProvider`     | Creates the SDK client and makes it available to hooks                                           |
| `KatlaGuard`        | Injects the cookie guard script that blocks non-consented cookies                                |
| `ConsentBridge`     | Connects `window.KatlaConsent` events to the React consent state                                 |
| `GoogleConsentMode` | Signals consent state to Google Analytics/Ads via [Google Consent Mode v2](/google-consent-mode) |

### KatlaProvider props

| Prop                | Type                 | Default                  | Description                                                                                                                            |
| ------------------- | -------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `siteId`            | `string`             | —                        | Your site's UUID (required)                                                                                                            |
| `baseUrl`           | `string`             | `https://dist.katla.app` | Override the CDN base URL                                                                                                              |
| `locale`            | `PolicyLocale`       | —                        | Default locale for policy documents (e.g., `en-GB`, `de-DE`, `sv-SE`). When set, all policy fetches use this locale unless overridden. |
| `debug`             | `boolean`            | `false`                  | Enable console logging                                                                                                                 |
| `initialCookies`    | `CookieData \| null` | —                        | Pre-fetched cookie data. When provided, `useKatlaCookies()` skips the runtime fetch.                                                   |
| `guardScript`       | `string`             | —                        | Pre-fetched guard script content. When provided, the guard is inlined instead of fetched from the CDN.                                 |
| `guard`             | `boolean`            | `true`                   | Inject the cookie guard script that blocks non-consented cookies                                                                       |
| `consentBridge`     | `boolean`            | `true`                   | Connect `window.KatlaConsent` events to the React consent state                                                                        |
| `googleConsentMode` | `boolean`            | `false`                  | Signal consent state to Google Analytics/Ads via [Google Consent Mode v2](/google-consent-mode)                                        |

## Hooks

### useKatlaCookies

Fetches your site's cookie data on mount.

```tsx theme={null}
import { useKatlaCookies } from '@katla.app/sdk/react';

function CookieList() {
  const { cookies, loading, error } = useKatlaCookies();

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  if (!cookies) return null;

  return (
    <ul>
      {Object.entries(cookies.cookies).map(([category, list]) => (
        <li key={category}>
          {category}: {list.length} cookies
        </li>
      ))}
    </ul>
  );
}
```

| Return field | Type                 | Description                     |
| ------------ | -------------------- | ------------------------------- |
| `cookies`    | `CookieData \| null` | Cookie data grouped by category |
| `loading`    | `boolean`            | `true` while fetching           |
| `error`      | `Error \| null`      | Fetch error, if any             |

### useKatlaConsent

Tracks the visitor's current consent state in real time.

```tsx theme={null}
import { useKatlaConsent } from '@katla.app/sdk/react';

function ConsentStatus() {
  const { consent } = useKatlaConsent();

  if (!consent) return <p>No consent recorded yet.</p>;

  return (
    <ul>
      {Object.entries(consent).map(([category, allowed]) => (
        <li key={category}>
          {category}: {allowed ? 'Allowed' : 'Declined'}
        </li>
      ))}
    </ul>
  );
}
```

| Return field | Type                       | Description                                       |
| ------------ | -------------------------- | ------------------------------------------------- |
| `consent`    | `ConsentState \| null`     | Current consent per category, or `null` if none   |
| `onChange`   | `(callback) => () => void` | Subscribe to consent changes; returns unsubscribe |

### useKatlaClient

Returns the underlying `KatlaClient` instance for direct API access.

```tsx theme={null}
import { useKatlaClient } from '@katla.app/sdk/react';

function ManifestLoader() {
  const client = useKatlaClient();

  async function loadManifest() {
    const manifest = await client.getManifest();
    console.log(manifest);
  }

  return <button onClick={loadManifest}>Load manifest</button>;
}
```

### useConsentManager

Headless hook that combines cookie data, consent state, and `window.KatlaConsent` actions into a single API. Use it to build fully custom consent UIs without boilerplate.

```tsx theme={null}
import { useConsentManager } from '@katla.app/sdk/react';

function MyConsentUI() {
  const {
    ready,               // true once window.KatlaConsent is available
    hasDecision,         // true after the visitor has accepted/rejected
    open,                // preferences panel visibility
    setOpen,
    selected,            // currently toggled-on categories
    availableCategories, // categories with cookies (excludes functional/unknown)
    toggleCategory,
    acceptAll,           // calls KatlaConsent.acceptAll() and closes the panel
    rejectAll,           // calls KatlaConsent.rejectAll() and closes the panel
    saveSelection,       // calls KatlaConsent.acceptCategories() and closes the panel
    cookies,             // { data, loading, error }
  } = useConsentManager();

  if (!ready) return null;

  return (
    <div>
      {!hasDecision && (
        <div>
          <p>We use cookies.</p>
          <button onClick={acceptAll}>Accept all</button>
          <button onClick={rejectAll}>Reject all</button>
          <button onClick={() => setOpen(true)}>Customize</button>
        </div>
      )}

      {open && (
        <div>
          {availableCategories.map((cat) => (
            <label key={cat}>
              <input
                type="checkbox"
                checked={selected.includes(cat)}
                onChange={() => toggleCategory(cat)}
              />
              {cat}
            </label>
          ))}
          <button onClick={saveSelection}>Save</button>
        </div>
      )}
    </div>
  );
}
```

| Return field          | Type                                | Description                                                        |
| --------------------- | ----------------------------------- | ------------------------------------------------------------------ |
| `ready`               | `boolean`                           | `true` once `window.KatlaConsent` is available                     |
| `hasDecision`         | `boolean`                           | `true` after the visitor has made a consent choice                 |
| `open`                | `boolean`                           | Whether the preferences panel is visible                           |
| `setOpen`             | `(open: boolean) => void`           | Toggle the preferences panel                                       |
| `selected`            | `ManageableCategory[]`              | Currently selected categories                                      |
| `availableCategories` | `ManageableCategory[]`              | Categories that have cookies (excludes `functional` and `unknown`) |
| `toggleCategory`      | `(cat: ManageableCategory) => void` | Toggle a category on/off                                           |
| `acceptAll`           | `() => void`                        | Accept all categories and close the panel                          |
| `rejectAll`           | `() => void`                        | Reject non-essential categories and close the panel                |
| `saveSelection`       | `() => void`                        | Save selected categories and close the panel                       |
| `cookies`             | `{ data, loading, error }`          | Cookie data from `useKatlaCookies()`                               |

The `ManageableCategory` type (`'personalization' | 'analytics' | 'marketing' | 'security'`) is also exported for type-safe category handling.

## Components

### CookieBanner

A ready-to-use consent banner with accept, reject, and per-category customization. The component is **unstyled** — it renders semantic HTML with stable `katla-*` class names you can target with your own CSS.

```tsx theme={null}
import { CookieBanner } from '@katla.app/sdk/react';

function App() {
  return (
    <KatlaProvider siteId="your-site-id">
      <CookieBanner />
    </KatlaProvider>
  );
}
```

Customize labels and copy:

```tsx theme={null}
<CookieBanner
  title="We use cookies"
  description="Choose which categories you'd like to allow."
  labels={{
    acceptAll: 'Accept all',
    rejectAll: 'Reject non-essential',
    saveSelection: 'Save selection',
    functionalBadge: 'Functional always on',
    privacyTrigger: 'Privacy preferences',
  }}
  className="my-banner"
/>
```

| Prop          | Type                                        | Default                | Description                          |
| ------------- | ------------------------------------------- | ---------------------- | ------------------------------------ |
| `title`       | `string`                                    | `"Cookie preferences"` | Banner heading                       |
| `description` | `ReactNode`                                 | Brief consent copy     | Descriptive text below the title     |
| `labels`      | `object`                                    | See above              | Button and badge labels              |
| `className`   | `string`                                    | —                      | Additional class on the root element |
| `children`    | `(state: ConsentManagerState) => ReactNode` | —                      | Render prop for full UI override     |

For a fully custom UI, use the `children` render prop. The default markup is replaced entirely:

```tsx theme={null}
<CookieBanner>
  {({ acceptAll, rejectAll, open, setOpen, selected, toggleCategory, saveSelection }) => (
    <div className="my-custom-banner">
      <button onClick={acceptAll}>OK</button>
      <button onClick={rejectAll}>No thanks</button>
      <button onClick={() => setOpen(!open)}>Customize</button>
    </div>
  )}
</CookieBanner>
```

CSS class names for styling the default UI:

| Class                    | Element                              |
| ------------------------ | ------------------------------------ |
| `katla-banner`           | Root `<aside>`                       |
| `katla-banner-copy`      | Title and description wrapper        |
| `katla-banner-controls`  | Button area in the preferences panel |
| `katla-toggle-grid`      | Category toggle container            |
| `katla-toggle-card`      | Individual category toggle           |
| `katla-pill`             | "Always on" badge                    |
| `katla-button-row`       | Row of action buttons                |
| `katla-button-accept`    | Accept button                        |
| `katla-button-reject`    | Reject button                        |
| `katla-button-save`      | Save selection button                |
| `katla-button-customize` | Customize button                     |
| `katla-privacy-trigger`  | Re-open button (shown after consent) |

### CookieCatalog

Displays your site's cookies grouped by category. Also unstyled with `katla-*` class names.

```tsx theme={null}
import { useKatlaCookies, CookieCatalog } from '@katla.app/sdk/react';

function CookieList() {
  const { cookies, loading, error } = useKatlaCookies();
  return <CookieCatalog data={cookies} loading={loading} error={error} />;
}
```

| Prop             | Type                               | Default               | Description                          |
| ---------------- | ---------------------------------- | --------------------- | ------------------------------------ |
| `data`           | `CookieData \| null`               | —                     | Cookie data from `useKatlaCookies()` |
| `loading`        | `boolean`                          | `false`               | Show loading state                   |
| `error`          | `Error \| string \| null`          | —                     | Show error state                     |
| `maxPerCategory` | `number`                           | `5`                   | Max cookies shown per category       |
| `className`      | `string`                           | —                     | Additional class on the root element |
| `emptyMessage`   | `string`                           | `"No cookies found."` | Message when no cookies exist        |
| `renderCategory` | `(category, cookies) => ReactNode` | —                     | Override a full category section     |
| `renderCookie`   | `(cookie, category) => ReactNode`  | —                     | Override a single cookie row         |

Customize per-cookie rendering while keeping the grid layout:

```tsx theme={null}
<CookieCatalog
  data={cookies}
  loading={loading}
  error={error}
  maxPerCategory={10}
  renderCookie={(cookie) => (
    <div>
      <code>{cookie.name}</code> — {cookie.domain}
    </div>
  )}
/>
```

CSS class names:

| Class                       | Element                                  |
| --------------------------- | ---------------------------------------- |
| `katla-catalog-grid`        | Root grid container                      |
| `katla-catalog-card`        | Category card                            |
| `katla-catalog-card-header` | Card header with category name and count |
| `katla-empty-card`          | Shown when no cookies exist              |

## Building a consent banner

The fastest way to add a consent banner is with the `<CookieBanner>` component — it handles all the consent logic out of the box. For full control, use the `useConsentManager` hook or call `window.KatlaConsent` methods directly.

<Note>
  `window.KatlaConsent` is provided by the guard script. It becomes available after `KatlaGuard` mounts and the script loads. The `ConsentBridge` component handles polling for it and relaying events to React.
</Note>

## Static cookies (build-time)

Use the Katla CLI to fetch cookie data at build time, then pass it as static data — no runtime fetch needed.

**1. Add to your build pipeline:**

```json theme={null}
{
  "scripts": {
    "prebuild": "katla pull your-site-id",
    "build": "vite build"
  }
}
```

**2. Import and pass to the provider:**

```tsx theme={null}
import cookies from './.katla/cookies.json';
import { KatlaProvider } from '@katla.app/sdk/react';

function App() {
  return (
    <KatlaProvider siteId="your-site-id" initialCookies={cookies}>
      {/* useKatlaCookies() returns data immediately — no fetch */}
    </KatlaProvider>
  );
}
```

See the [SDK overview](/sdk#static--build-time-cookies) for CLI reference and the programmatic API.

## Server-side rendering (SSR)

If your framework supports server-side rendering (Remix, Astro, etc.), fetch cookies on the server and pass them to the provider to avoid a client-side network request:

```tsx theme={null}
// Server-side (e.g. Remix loader)
import { createKatlaClient } from '@katla.app/sdk';

const client = createKatlaClient({ siteId: 'your-site-id' });
const cookies = await client.getCookies();
```

```tsx theme={null}
// Client component
import { KatlaProvider } from '@katla.app/sdk/react';

function App({ cookies }) {
  return (
    <KatlaProvider siteId="your-site-id" initialCookies={cookies}>
      {/* useKatlaCookies() returns data immediately — no fetch */}
    </KatlaProvider>
  );
}
```

The guard script and consent bridge still run client-side. Only the cookie data fetch is moved to the server.

## Debug mode

Pass `debug` to `KatlaProvider` to log SDK activity to the browser console:

```tsx theme={null}
<KatlaProvider siteId="your-site-id" debug>
```

## Full example

A minimal Vite + React app with cookie catalog and consent banner:

```tsx theme={null}
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import {
  KatlaProvider,
  CookieBanner,
  CookieCatalog,
  useKatlaCookies,
} from '@katla.app/sdk/react';

function App() {
  const { cookies, loading, error } = useKatlaCookies();

  return (
    <div>
      <h1>Cookie consent</h1>
      <CookieCatalog data={cookies} loading={loading} error={error} />
      <CookieBanner />
    </div>
  );
}

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <KatlaProvider siteId="your-site-id" debug>
      <App />
    </KatlaProvider>
  </StrictMode>,
);
```

## Examples

<CardGroup cols={3}>
  <Card title="Dynamic fetching" icon="github" href="https://github.com/katla-app/examples/tree/main/vite-sdk-dynamic">
    Vite + React app that fetches cookies and policy at runtime.
  </Card>

  <Card title="Build-time static" icon="github" href="https://github.com/katla-app/examples/tree/main/vite-sdk-static">
    Vite + React app using `katla pull` for build-time cookie data.
  </Card>

  <Card title="Custom consent UI" icon="github" href="https://github.com/katla-app/examples/tree/main/vite-sdk-custom">
    Vite + React app with a fully custom consent UI using `useConsentManager`.
  </Card>
</CardGroup>
