---
title: Callbacks
description: React to consent lifecycle events — initialization, consent changes, errors, and revocation reloads.
---
<import src="../../shared/react/guides/callbacks.mdx#intro" />

> ℹ️ **Info:**
> consentStore.getState().subscribeToConsentChanges() is the recommended API for analytics SDKs and consent-mode integrations. It only emits future saves that actually changed persisted preferences.

## Configuration

Define callbacks in the runtime options:

```ts
import { getOrCreateConsentRuntime } from 'c15t';

const { consentStore } = getOrCreateConsentRuntime({
  mode: 'hosted',
  backendURL: 'https://your-instance.c15t.dev',
  callbacks: {
    onBannerFetched: ({ jurisdiction, location, translations }) => {
      console.log('Jurisdiction:', jurisdiction);
      console.log('Country:', location.countryCode);
      console.log('Language:', translations.language);
    },
    onConsentSet: ({ preferences }) => {
      console.log('Consent lifecycle event:', preferences);
    },
    onConsentChanged: ({ allowedCategories, deniedCategories }) => {
      analytics.syncConsent({ allowedCategories, deniedCategories });
    },
    onError: ({ error }) => {
      errorReporter.captureMessage(error);
    },
    onBeforeConsentRevocationReload: ({ preferences }) => {
      // Flush pending analytics before page reloads
      analytics.flush();
    },
  },
});
```

<import src="../../shared/react/guides/callbacks.mdx#callback-reference" />

## Change-Only Subscriptions

Use `subscribeToConsentChanges()` when you want a listener for real preference changes after runtime creation:

```ts
const unsubscribe = consentStore
  .getState()
  .subscribeToConsentChanges(({ allowedCategories, deniedCategories }) => {
    analytics.syncConsent({ allowedCategories, deniedCategories });
  });

// Later, when you're done listening:
unsubscribe();
```

## Runtime Callback Registration

Register or update callbacks at runtime using `setCallback()`:

```ts
const state = consentStore.getState();

state.setCallback('onBannerFetched', ({ jurisdiction, location }) => {
  console.log('Resolved init data:', { jurisdiction, location });
});

state.setCallback('onConsentSet', ({ preferences }) => {
  console.log('Broad consent lifecycle event:', preferences);
});

// Remove callbacks again when no longer needed
state.setCallback('onBannerFetched', undefined);
state.setCallback('onConsentSet', undefined);
```

`setCallback('onConsentSet', ...)` immediately replays the current consent state. For change-only logic, prefer `subscribeToConsentChanges()` or `onConsentChanged`.
