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

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

## Configuration

```tsx
import { type ReactNode } from 'react';
import { ConsentManagerProvider } from '@c15t/react';

export function ConsentManager({ children }: { children: ReactNode }) {
  return (
    <ConsentManagerProvider
      options={{
        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();
          },
        },
      }}
    >
      {children}
    </ConsentManagerProvider>
  );
}
```

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

## Change-Only Subscriptions

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

```tsx
import { useEffect } from 'react';
import { useConsentManager } from '@c15t/react';

function ConsentAnalytics() {
  const { subscribeToConsentChanges } = useConsentManager();

  useEffect(() => {
    return subscribeToConsentChanges(({ allowedCategories, deniedCategories }) => {
      analytics.syncConsent({ allowedCategories, deniedCategories });
    });
  }, [subscribeToConsentChanges]);

  return null;
}
```

## Runtime Callback Registration

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

```tsx
import { useEffect } from 'react';
import { useConsentManager } from '@c15t/react';

function ConsentAnalytics() {
  const { setCallback } = useConsentManager();

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

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

    return () => {
      setCallback('onBannerFetched', undefined);
      setCallback('onConsentSet', undefined);
    };
  }, [setCallback]);

  return null;
}
```

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