React Native Consent UI
Headless consent management for React Native apps using @transcend-io/react-native-ui, including initialization, consent APIs, sync, and SDK status checks.
Transcend React Native UI SDK Guide
The Transcend React Native UI SDK provides a consent management banner API for iOS and Android apps. Import the package as @transcend-io/cm-mobile-ui-react-native. All Transcend API calls are accessed through the useConsent or useConsentUI hooks inside a ConsentProvider.
Requirements: Migration off of Legacy Banners and onto Consent Experiences, React Native 0.75+, React 18+, react-native-svg
Architecture note
ConsentProvider keeps only two pieces of React state on top of headless usage — consent and isReady. All other non-display SDK methods (setConsent, getSdkConsentStatus, etc.) are passthroughs to the native Transcend Headless SDK.
Installation
Install the package and its peer dependency:
yarn add @transcend-io/cm-mobile-ui-react-native react-native-svg
# or
npm install @transcend-io/cm-mobile-ui-react-native react-native-svgiOS
Install pods after adding the package:
cd ios && pod installThe native iOS Headless SDK is pulled in automatically via Swift Package Manager.
Android
No extra repository configuration is required. The native Android Headless SDK (io.transcend.sdk.headless:headless) is included as a transitive dependency. INTERNET and ACCESS_NETWORK_STATE permissions are declared automatically.
Initialization
Wrap your app in ConsentProvider and pass a config object. The provider initializes the native SDK on mount and exposes readiness through isReady.
import { ConsentProvider } from '@transcend-io/cm-mobile-ui-react-native';
export default function App() {
return (
<ConsentProvider
config={{
bundleId: 'YOUR_BUNDLE_ID', // Required — Transcend bundle ID
mobileAppId: 'com.example.myapp', // Required — your app's mobile identifier from https://app.transcend.io/consent-manager/native/applications
isTest: false, // Use test or production deployment
cdnUrl: 'https://transcend-cdn.com', // Optional — defaults to Transcend's CDN
token: 'AUTH_TOKEN', // Optional — auth token for backend sync
settingsOverrides: {
// Optional — override remote settings at init
country: 'EU',
regime: 'GDPR',
},
}}
onError={(error) => {
// Fires when SDK initialization fails
console.error('Consent SDK init failed:', error);
}}
>
<YourApp />
</ConsentProvider>
);
}Handling initialization errors
Pass an onError callback to ConsentProvider to be notified when initialization fails (e.g. invalid config, network error, or native SDK init rejection). When onError fires, isReady remains false and consent APIs should not be called.
<ConsentProvider
config={{ bundleId: '...', mobileAppId: '...' }}
onError={(error) => {
// Log, report to your error tracker, or show a fallback UI
reportError('consent_init_failed', error);
}}
>
<YourApp />
</ConsentProvider>onError is invoked once per failed init attempt, use it alongside the isReady check in child components. isReady tells you when it is safe to proceed; onError tells you why initialization did not succeed.
config fields (InitSdkOptions)
| Field | Required | Description |
|---|---|---|
bundleId | Yes | Transcend bundle ID. |
mobileAppId | Yes | Native app identifier registered in Transcend. |
isTest | No | Use test deployment. Default: false. |
cdnUrl | No | CDN base URL. Default: https://transcend-cdn.com. |
token | No | Auth token for backend sync. Required for sync to work. |
settingsOverrides | No | Runtime overrides for remote settings (see Registering Overrides). |
destroyOnClose | No | Tear down the native SDK when the provider unmounts. Default: false. |
Checking Readiness
Use the isReady signal from useConsent to know when the SDK has finished loading settings and (optionally) completed its initial sync. Do not call consent APIs until isReady is true.
import { useEffect } from 'react';
import { useConsent } from '@transcend-io/cm-mobile-ui-react-native';
function TrackerLoader() {
const { isReady, getSdkConsentStatus } = useConsent();
useEffect(() => {
if (!isReady) return;
void getSdkConsentStatus('analytics_sdk').then((status) => {
if (status === 'ALLOW') {
// Safe to initialize the analytics tracker
}
});
}, [isReady, getSdkConsentStatus]);
return null;
}If initialization fails, isReady remains false and the onError callback passed to ConsentProvider is invoked with the error. Use onError to log or surface the failure; gate all consent and tracker logic on isReady.
Displaying the Banner
Once the SDK has been properly initialized and isReady is returning true, use the autoShowConsentManager and showConsentManager methods in the useConsentUI context in order to display the banner. Unlike web airgap.js implementations, mobile flows will not automatically display banners on initialization.
const {
showConsentManager, // Open the bundled consent UI
hideConsentManager, // Close the bundled consent UI
autoShowConsentManager, // Show UI if auto-prompt conditions are met
} = useConsentUI();
showConsentManager(); // Opens default experience
showConsentManager({ viewState: 'banner-into-modal' }); // Opens a specific experienceGetting and Setting Consent
Get consent
Read the current consent from the consent object exposed by useConsent. This is React state kept in sync after initialization, sync, and after setConsent calls.
const { consent } = useConsent();
// consent.confirmed — boolean
// consent.prompted — boolean
// consent.purposes — Record<string, boolean>
// consent.timestamp — ISO 8601 string of when consent was givenSet consent
Pass a map of purpose names to boolean values. Syncs to the backend by default when a token was provided at init.
const { setConsent } = useConsent();
const success = await setConsent(
{
Analytics: true,
Functional: true,
Advertising: false,
},
{
autoSync: true, // Sync to backend after setting (default: true)
confirmed: true, // Mark consent as confirmed (default: true)
timestamp: undefined, // ISO Timestamp associated with the consent change (default: now)
updated: true, // Mark consent as updated (default: true)
}
);After setConsent resolves, the consent object in context is refreshed automatically.
Registering Overrides
Pass overrides in config.settingsOverrides at initialization to test specific regimes, disable sync, or force configuration values.
<ConsentProvider
config={{
bundleId: 'YOUR_BUNDLE_ID',
mobileAppId: 'com.example.myapp',
settingsOverrides: {
regime: 'GDPR',
},
}}
>
<YourApp />
</ConsentProvider>Common override keys include: "partition", "regime", "defaultRegime".
Syncing Consent
Backend sync requires an auth token. Pass token in config at init, otherwise sync is skipped and consent is not pushed to or pulled from the backend. Generate a token on your backend server following the Transcend Preference Store docs.
<ConsentProvider
config={{
bundleId: 'YOUR_BUNDLE_ID',
mobileAppId: 'com.example.myapp',
token: 'AUTH_TOKEN', // Required for sync
}}
>
<YourApp />
</ConsentProvider>The native SDK syncs consent automatically in two cases:
- During initialization using the
tokenfromconfig. - After
setConsentwhenautoSyncistrue(the default).
There is no separate public sync() method. To trigger a sync, call setConsent with autoSync: true and ensure a valid token was provided at init.
Sync is also skipped when SYNC_DISABLED is true in an override.
After a successful sync during init, isReady becomes true and consent reflects any remote changes.
SDK Consent Status
Use these APIs to determine whether a third-party SDK (identified by a service ID) is allowed to run given the current consent state. Gate tracker initialization on isReady, then check status before loading each SDK.
Recommended pattern
import { useEffect } from 'react';
import { useConsent } from '@transcend-io/cm-mobile-ui-react-native';
function AnalyticsTracker() {
const { isReady, getSdkConsentStatus } = useConsent();
useEffect(() => {
if (!isReady) return;
void getSdkConsentStatus('analytics_sdk').then((status) => {
if (status === 'ALLOW') {
// Initialize analytics SDK
}
});
}, [isReady, getSdkConsentStatus]);
return null;
}Re-run tracker checks whenever isReady transitions to true (e.g. after init) or when consent changes and you need to re-evaluate SDK permissions.
Single service
const { getSdkConsentStatus } = useConsent();
const status = await getSdkConsentStatus('analytics_sdk');
switch (status) {
case 'ALLOW':
// SDK may run
break;
case 'BLOCK':
// User denied required purpose(s)
break;
case 'NO_SDK_FOUND':
// Service ID not in purpose map
break;
case 'INTERNAL_ERROR':
// Error occurred
break;
}Batch lookup
Pass an array of service IDs to evaluate multiple SDKs at once:
const { batchGetSdkConsentStatuses } = useConsent();
const statusMap = await batchGetSdkConsentStatuses([
'analytics_sdk',
'ads_sdk',
]);
// e.g. { analytics_sdk: 'ALLOW', ads_sdk: 'BLOCK' }ConsentStatus values
| Status | Meaning |
|---|---|
ALLOW | All required purposes for this SDK are granted (or SDK is Essential). |
BLOCK | At least one required purpose was explicitly denied. |
NO_SDK_FOUND | The service ID is not in the SDK purpose map. |
INTERNAL_ERROR | Consent resolution encountered an error while reconciling the service ID's purposes. |
The SDK purpose map is loaded from settings and cached by the native SDK (refreshed every 3 hours).
Direct Headless Usage
For direct native access without the React context (advanced usage), import NativeConsent from the package. This bypasses the consent / isReady React state and should only be used when you want to manage headless state yourself.
Error Handling
| Scenario | Behavior |
|---|---|
Missing bundleId or mobileAppId | onError invoked; isReady stays false. |
| Native init failure | onError invoked with the error; also logged to console; isReady stays false. |
| API called before ready | Native SDK may throw; always gate on isReady first. |
useConsent outside ConsentProvider | Throws: "useConsent must be used inside ConsentProvider". |