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

# React Native

> Consent banner, preferences, consent records and vendor control for React Native and Expo apps.

Documentation: [React Native SDK](https://docs.cookiechimp.com/docs/sdk/react-native.md).

Consent banner, preferences screen, consent records and vendor control for
React Native and Expo apps, built on the same `@cookiechimp/core` consent engine
as the CookieChimp web SDK.

This package is part of the coordinated CookieChimp SDK release. Keep any
CookieChimp packages you use on matching versions. See
[release status](https://github.com/cookiechimp/cookiechimp-sdk/blob/main/docs/remaining-work.md).

## What it does

* Shows the banner and preferences you design in the CookieChimp dashboard as
  native screens, with your copy, translations, colors and button settings.
* Picks the right banner for the user's country or US state and applies GDPR
  (opt-in) or CCPA (opt-out) rules from the banner's consent mode.
* Stores the decision on the device and asks again when it expires or when you
  require re-consent in the dashboard.
* Sends consent records to CookieChimp, queued and retried while offline.
* Starts and stops your analytics and advertising SDKs as consent changes.
* Needs no custom native code beyond AsyncStorage, so it works in Expo Go,
  development builds and bare React Native apps.

## Install

```bash theme={null}
npm install @cookiechimp/react-native @react-native-async-storage/async-storage
```

With Expo, use `npx expo install @cookiechimp/react-native @react-native-async-storage/async-storage`.
In a bare app, run `npx pod-install` afterwards. React Native 0.80 or newer
(Expo SDK 54 or newer) and React 18 or newer are supported. Older React Native
versions ship an incomplete `URL` implementation: install
`react-native-url-polyfill` and import `react-native-url-polyfill/auto` first.

## Quick start

Wrap your app with `CookieChimpProvider` and your account ID from the dashboard.

```tsx theme={null}
import { CookieChimpProvider } from "@cookiechimp/react-native";

export default function App() {
  return (
    <CookieChimpProvider accountId="YOUR_ACCOUNT_ID">
      <RootNavigator />
    </CookieChimpProvider>
  );
}
```

Your app renders immediately. The banner slides in once the stored decision and
the dashboard config are loaded, and only when the user still needs to choose.

## Connected and offline modes

**Connected mode** (`accountId`) downloads the dashboard config, caches it on the
device and renders from the cache on later launches. It revalidates with an ETag
in the background and again when the app returns to the foreground after five
minutes, so dashboard changes reach users without an app release.

**Offline mode** (`config`) uses a config file you download from the dashboard
(Setup, Download Config) and ship inside the app. No config requests are made.
Consent records are still delivered when the device is online.

```tsx theme={null}
import config from "./cookiechimp-config.json";

<CookieChimpProvider config={config}>{children}</CookieChimpProvider>;
```

To update an offline config, download the file again and ship an app update.

## Region and language

The region selects which dashboard banner applies and whether its opt-out
(CCPA) rules can be used. By default the SDK never looks up the user's location.

1. `country="DE"` or `country="US-CA"` always wins. Pass it when your app
   already knows the user's country, for example from your own backend.
2. Otherwise, with `geolocation` and an `accountId`, the SDK asks CookieChimp's
   own first-party endpoint for the connection's country (one small IP-based
   request on launch). No third-party location service is contacted and device
   location permissions are never used. This is off unless you opt in.
3. Without a region, the dashboard's default banner is shown with the safe GDPR
   fallback: optional categories stay off until the user chooses, even when the
   default banner is an opt-out banner.

Device locale (for example `en-US`) is a language setting, not a location, and
is never used to choose the region.

The language defaults to the device language and falls back to the first
language the banner has. Override it with `language="fr"`. See
[Translations](#translations) for how missing copy is filled in.

## Provider props

| Prop | Description |
| - | - |
| `accountId` | Your CookieChimp account ID (connected mode). |
| `config` | The downloaded config JSON (offline mode). |
| `country` | `DE`, `US-CA` and so on. Overrides region detection. |
| `language` | Banner language. Defaults to the device language. |
| `geolocation` | Opt in to asking CookieChimp for the country when `country` is not set. Default `false`. |
| `syncConsents` | Send consent records to CookieChimp. Default `true`. |
| `ui` | Render the built-in banner and preferences. Set `false` for [headless mode](#headless-mode). |
| `insets` | Safe-area insets for the consent UI, for example from `react-native-safe-area-context`. |
| `storage` | An AsyncStorage-compatible store, for example an MMKV adapter. |
| `debug` | Log SDK activity to the console and show the [debug panel](#debugging). Use `debug={__DEV__}`. |
| `debugPanel` | Show or hide the debug panel independently of `debug`. |
| `translations` | Your own copy for the SDK's built-in text, by language. See [Translations](#translations). |
| `onFirstConsent` | Called after the first decision on this install. |
| `onAccept` | Called after every decision, and on launch when a valid decision is stored. |
| `onChange` | Called after a later decision, with the changed category and service ids. |

## Reading and changing consent

Use hooks inside the provider:

```tsx theme={null}
import { useCategory, usePreferences } from "@cookiechimp/react-native";

function Settings() {
  const analytics = useCategory("analytics");
  const preferences = usePreferences();
  return (
    <Button title="Privacy settings" onPress={preferences.show} />
  );
}
```

| Hook | Returns |
| - | - |
| `useConsent()` | The consent state (`categories`, `services`, `consentId`, `ts`, `action`), or `null` while loading. |
| `useCategory(id)` | `true` when the category is allowed. |
| `useCategories()` | Categories with labels, descriptions, `required`, `enabled` and their `services` (name, company, privacy policy, data labels, `enabled`). |
| `useConsentText()` | All banner and preferences copy with translated fallbacks, or `null` while loading. |
| `useConsentDraft()` | Pending choices for your own preferences screen: `categories`, `toggleCategory`, `toggleService`, `save`, `discard`. |
| `useConsentActions()` | `acceptAll`, `rejectAll`, `save({ categories, services })`, `reset`. |
| `useBanner()` / `usePreferences()` | `{ open, show, hide }`. |
| `useOnConsentChange(fn)` | Calls `fn` after each new decision. |
| `useRegion()` | `{ region, regulation }`. |
| `useCookieChimp()` | The full SDK state, including `status` and the dashboard config. |

Or use the `CookieChimp` object anywhere, including outside React:

```ts theme={null}
import { CookieChimp } from "@cookiechimp/react-native";

CookieChimp.show();
CookieChimp.showPreferences();
CookieChimp.acceptCategory("all");
CookieChimp.acceptCategory([]); // necessary only
CookieChimp.acceptCategory(["analytics"]);
CookieChimp.acceptService(["Firebase Analytics"], "analytics");
CookieChimp.acceptedCategory("analytics"); // boolean
CookieChimp.acceptedService("Firebase Analytics", "analytics"); // boolean
CookieChimp.getUserPreferences();
const stop = CookieChimp.onChange((preferences, categories, services) => {});
CookieChimp.reset(); // forget the decision and show the banner again
```

`CookieChimp.run(options)` starts the SDK without the provider, for example in
`index.js` before React mounts. Render `<CookieChimpProvider>` without
`accountId` or `config` to show the built-in UI for an SDK started this way.

## Controlling vendor SDKs

Register each analytics or advertising SDK with the category it belongs to. The
SDK calls `onStart` when consent allows it and `onStop` when consent is
withdrawn. Nothing starts before consent where consent is required.

```ts theme={null}
import analytics from "@react-native-firebase/analytics";
import { CookieChimp } from "@cookiechimp/react-native";

CookieChimp.registerVendor("Firebase Analytics", "analytics", {
  onStart: () => analytics().setAnalyticsCollectionEnabled(true),
  onStop: () => analytics().setAnalyticsCollectionEnabled(false),
});
```

When the name matches a service in your dashboard, the user's choice for that
service applies; otherwise the category's choice applies. Disable each SDK's
automatic collection in its native configuration so nothing is sent before
`onStart`. `getVendors()`, `isVendorRunning(name)`, `startVendor(name)`,
`stopVendor(name)` and `unregisterVendor(name)` are also available.

Presets do this for Firebase, AppsFlyer, Adjust, Meta, Amplitude, Mixpanel,
Branch, Braze and OneSignal, using the names from CookieChimp's mobile SDK
library:

```ts theme={null}
import { CookieChimp, connectFirebase, presets } from "@cookiechimp/react-native";

CookieChimp.registerVendor(presets.mixpanel(mixpanel));
CookieChimp.registerVendor(presets.appsFlyer(appsFlyer));

// Analytics, Crashlytics and Performance collection, plus Consent Mode v2.
connectFirebase({ analytics: analytics(), crashlytics: crashlytics() });
```

On iOS, `requestTrackingAfterConsent` shows the App Tracking Transparency
prompt after the user accepts marketing. See
[vendor SDKs, Firebase and App Tracking Transparency](https://docs.cookiechimp.com/docs/sdk/react-native-integrations.md)
for every preset, the Firebase setup and ATT guidance.

## Design settings

The built-in screens follow the banner design you set in the dashboard:

| Dashboard setting | In the app |
| - | - |
| Layout: Box, Cloud, Bar | A floating card, a wider card or an edge-to-edge bar. |
| Layout: Full Screen (mobile apps) | A full-screen consent page with the buttons pinned at the bottom. |
| Banner position | Top, middle or bottom of the screen; left and right on tablets. |
| Button placement | Stacked, inline, or inline wide. |
| Equal weight buttons, swap buttons | Reject uses the primary style; button order follows the dashboard. |
| Show accept, reject, preferences and close buttons | Each button is shown or hidden. |
| Force consent | The app is dimmed and the banner cannot be dismissed. |
| Colors, dark mode colors, dark mode (auto, light, dark) | Every color, following the device's appearance in auto. |
| Banner and button corner radius | Corner radius of the cards and buttons (`px`, `rem` and `em`). |
| Light and dark logo | Shown at the top of the banner and preferences. |
| Footer content, powered by | Links in the footer, and a "Powered by CookieChimp" line at the foot of the banner and preferences on plans that show it. |
| Preferences layout: Box or Bar, left or right | A centered card or a full-height panel that slides in from the side. With the Full Screen banner layout, the preferences open as a full screen page too. |
| Consent at category level only, show services | Vendor switches, or vendor details without switches. |
| Floating preferences icon | Not shown in apps. Open the preferences from your own settings screen with `CookieChimp.showPreferences()`. |

Categories and vendors expand with an animation to show their purpose, the
company and the privacy policy. Banners and preferences animate in and out, and
animations are skipped when the device asks for reduced motion.

### Vendor data labels

Mobile SDKs do not set cookies, so each vendor shows its data labels from the
dashboard instead, in the same shape as Apple's App Privacy details and Google
Play's Data safety section:

| Label | Example |
| - | - |
| Data collected | Device ID, App interactions, Crash data |
| Used for | Analytics, App functionality |
| Tracks you across other apps | No |
| Kept for | 14 months |
| SDK packages | iOS `FirebaseAnalytics`, Android `com.google.firebase:firebase-analytics` |

SDKs added from the dashboard's library come with these filled in, and you can
edit them per SDK. The labels are shown in the banner's language.

## Headless mode

Set `ui={false}` to draw the banner and preferences yourself. The SDK still
loads the config, picks the banner and regulation, stores decisions, sends
consent records and starts and stops vendors; your screens read and change
consent with hooks.

```tsx theme={null}
import {
  CookieChimp,
  useBanner,
  useConsentDraft,
  useConsentText,
  usePreferences,
} from "@cookiechimp/react-native";

function MyBanner() {
  const { open } = useBanner();
  const text = useConsentText();
  if (!open || !text) return null;
  return (
    <Sheet title={text.banner.title} body={text.banner.description}>
      <Button title={text.banner.acceptAll} onPress={() => CookieChimp.acceptCategory("all")} />
      <Button title={text.banner.rejectAll} onPress={() => CookieChimp.acceptCategory([])} />
      <Button title={text.banner.customize} onPress={CookieChimp.showPreferences} />
    </Sheet>
  );
}

function MyPreferences() {
  const { open, hide } = usePreferences();
  const text = useConsentText();
  const draft = useConsentDraft();
  if (!open || !text) return null;
  return (
    <Sheet title={text.preferences.title} onClose={hide}>
      {draft.categories.map((category) => (
        <Row
          key={category.id}
          label={category.label}
          value={category.enabled}
          disabled={category.required}
          onChange={(on) => draft.toggleCategory(category.id, on)}
        />
      ))}
      <Button title={text.preferences.save} onPress={draft.save} />
    </Sheet>
  );
}
```

* `useBanner()` opens when the user still needs to decide, exactly as the
  built-in banner would. Keep your banner on screen until a decision.
* `useConsentText()` gives every string from the dashboard with translated
  fallbacks, the privacy policy URL, `rtl`, and whether the banner should
  show "Powered by CookieChimp" (`banner.poweredBy`). The preferences screen
  always shows it.
* Descriptions are dashboard rich text. `richText(html)` returns paragraphs of
  spans with `bold`, `italic` and safe `href` values; `plainText(html)` strips
  the markup.
* `useConsentDraft()` keeps changes local until `save()`. Switching a category
  switches its services; required categories cannot be switched off. Each
  category has `services` with `appData` labels; `text.messages.dataType(key)`
  and `text.messages.purpose(key)` name them in the user's language.

You can also render the built-in `<ConsentBanner />` and
`<ConsentPreferences />` yourself and replace only one of them.

## Translations

Every piece of text comes from your dashboard first, in the selected
language. When a field is empty, or for text the SDK draws itself (data
labels, "Privacy policy", "Your consent ID", "Powered by"), the SDK uses its
own copy in the same language, then the copy in the banner's default
language, then English. Optional copy such as the additional description is
never borrowed from another language.

Built-in copy is included for the 33 languages of the web SDK: Arabic,
Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, English, Estonian,
Finnish, French, German, Greek, Hebrew, Hungarian, Irish, Italian, Japanese,
Korean, Latvian, Lithuanian, Maltese, Norwegian, Polish, Portuguese
(Portugal and Brazil), Romanian, Slovak, Slovenian, Spanish, Swedish,
Turkish and Ukrainian. A device locale refines the dashboard language, so a
`pt-BR` device sees Brazilian Portuguese with a `pt` banner.

Replace any built-in string with `translations`. Define it outside your
component, since a new object restarts the SDK:

```tsx theme={null}
const translations = {
  de: { ui: { poweredBy: "Mit {name}" }, data: { device_id: "Gerätekennung" } },
  is: { ui: { acceptAll: "Samþykkja allt", rejectAll: "Hafna valfrjálsu" } },
};

<CookieChimpProvider accountId="YOUR_ACCOUNT_ID" translations={translations} />;
```

`messages(language)` returns the SDK copy for any language, and
`SDK_LANGUAGES` lists the built-in ones.

## Consent records

Each decision is stored on the device first and then sent to CookieChimp
(`POST /api/v1/mobile-sdk/sync/{accountId}`) with the platform (`ios` or
`android`), banner, language, accepted and rejected categories and services,
and the decision time. Device identifiers, user IDs and advertising IDs are
never sent. Records wait in a queue of up to 50 while the device is offline,
retry with backoff and are sent again when the app returns to the foreground.
Delivery is idempotent, so a retry never creates a duplicate.

## Storage

The decision, the cached config and queued records are stored in AsyncStorage
under keys starting with `@cookiechimp:`. Pass `storage` to use another store
with the same `getItem`, `setItem`, `removeItem` and `getAllKeys` methods.

## Debugging

`debug` logs the selected banner, region, language, regulation and every
decision to the console, and shows a small "CC debug" tab at the top of the
screen. Tap it to open the debug panel:

* SDK status, where the config came from (bundled file, device cache or
  network), config version, account, banner, language, region, regulation
  and the number of consent records waiting to be sent.
* The stored decision: action, consent ID, time, and every category and
  service.
* Registered vendors and whether each is running.
* Recent activity: config loads, decisions, re-consent, vendor starts and
  stops, and record delivery.
* Buttons to show the banner or preferences, accept all, reject all, reset
  consent and send records now.

Turn it off in release builds with `debug={__DEV__}`. `useDebugEvents()`
returns the same activity log, and `<DebugPanel />` renders the panel
yourself (for example with `CookieChimp.run`). `CookieChimp.reset()` clears
the stored decision so you can test the first-launch flow again.

MIT licensed.
