npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@transcend-io/cm-mobile-ui-react-native

v0.9.2

Published

Transcend Consent Manager mobile UI SDK for React Native

Readme

Transcend Headless React Native SDK Guide

The Transcend Headless React Native SDK provides a headless consent management API for iOS and Android apps. Import the package as @transcend-io/cm-mobile-ui-react-native. All headless API calls are accessed through the useConsent hook inside a ConsentProvider.

Requirements: React Native 0.75+, React 18+, react-native-svg, react-native-safe-area-context

Architecture support

| React Native version | New Architecture | Old Architecture (legacy bridge) | | -------------------- | ---------------- | -------------------------------- | | 0.75 – 0.81 | Supported | Supported | | 0.82+ | Supported | Not available (RN removed Old Architecture) |

The SDK registers its native module for both TurboModules (New Architecture) and the legacy bridge (Old Architecture). No extra configuration is required beyond a normal install and a full native rebuild after adding or updating the package.

Architecture note: ConsentProvider renders children immediately while the native SDK and UI assets load. Gate consent APIs and trackers on isReady from useConsent. The bundled consent banner/modal mounts only after translations and UI config are ready.


Installation

Install @transcend-io/cm-mobile-ui-react-native from the public npm registry like any other scoped package.

Dependencies

Add the SDK, peer packages, and Transcend dependencies to your app package.json:

{
  "dependencies": {
    "@react-native-async-storage/async-storage": "^2.0.0",
    "@transcend-io/cm-mobile-ui-react-native": "^0.9.0",
    "react-native-safe-area-context": "^5.0.0",
    "react-native-svg": "^15.0.0"
  }
}

Then install:

yarn add @transcend-io/cm-mobile-ui-react-native @react-native-async-storage/async-storage react-native-svg react-native-safe-area-context
# or
npm install @transcend-io/cm-mobile-ui-react-native @react-native-async-storage/async-storage react-native-svg react-native-safe-area-context

@react-native-async-storage/async-storage is an optional peer dependency, but required for offline CDN caching (load options, translations, and logo images). Without it, the SDK uses network-only fetches.

iOS

The native iOS Headless SDK is pulled in automatically via Swift Package Manager. CocoaPods links it into the pod target but does not embed the dynamic framework in your app, so you must add a one-time Podfile hook from the shipped setup script.

  1. Install pods after adding the package:
cd ios && pod install
  1. At the top of ios/Podfile (after the React Native / Expo requires), require the setup script:
transcend_pkg = File.dirname(
  `node --print "require.resolve('@transcend-io/cm-mobile-ui-react-native/package.json')"`
)
require File.join(transcend_pkg, 'setup-scripts/ios/embed_transcend_headless_framework')
  1. Inside your Podfile's app target block, add a post_integrate hook (alongside any existing post_install):
post_integrate do |installer|
  TranscendConsentUiReactNative::Ios.embed_headless_framework!(installer)
end
  1. Run pod install again so the hook patches the embed-frameworks script.

Without this hook, the app may build but crash at launch with a dyld error loading TranscendHeadless.framework.

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.

TurboModule codegen

The npm package ships TurboModule spec files under codegen/ (not src/). React Native autolinking reads codegenConfig.jsSrcsDir from the package manifest and runs codegen against that folder during your app build — no extra paths or tarball unpacking steps are required.

Install from a registry version or a local tarball:

yarn add @transcend-io/cm-mobile-ui-react-native
# or
yarn add /path/to/transcend-io-cm-mobile-ui-react-native-0.9.0.tgz

After adding the dependency, rebuild native projects (pod install on iOS, Gradle sync/build on Android) so codegen picks up the spec.

Troubleshooting: native module not found

If initialization fails with an error like:

[ConsentSDK] The native module "TranscendConsentUiReactNative" could not be found.

or the older React Native message:

TurboModuleRegistry.getEnforcing(...): 'TranscendConsentUiReactNative' could not be found

work through this checklist:

  1. Rebuild the native app — restarting Metro alone is not enough after installing or upgrading the package.
  2. iOS: run pod install and confirm the post_integrate embed hook from the iOS setup section is in your Podfile.
  3. Android: run a Gradle sync/build so autolinking picks up the package.
  4. Use a custom native build — Expo Go does not include this SDK. Use a dev client or bare React Native app.
  5. Confirm React Native >= 0.75 and that you are on a supported architecture (see Architecture support above). On RN 0.82+, Old Architecture is not available; a module-not-found error on those versions usually means a build or linking issue, not an architecture mismatch.

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 | Android native config flag (default: false). Does not destroy on React unmount — call destroy() from useConsent when you need teardown. |

Memoize the config object (especially settingsOverrides) when values are stable across renders. ConsentProvider only re-initializes when init fields change, not when the config object identity changes.

Offline CDN caching (cacheTtlMs)

ConsentProvider fetches ui-mobile-load-options.json once and uses that payload for both the consent UI and native SDK init, so the native layer does not make a second CDN request. Locale translation JSONs and consent UI logo images are cached the same way. When offline, the SDK falls back to the last cached copy.

cacheTtlMs controls this shared fetch (and the other JS-layer CDN assets).

| cacheTtlMs | Behavior | | ------------ | -------- | | omitted (default) | Caching on; entries do not expire. | | > 0 (e.g. 10800000 for 3h) | Caching on; offline fallback only uses entries younger than the TTL. Network refresh is still attempted when online. | | 0 | Caching off; network-only behavior. |

<ConsentProvider
  config={{ bundleId: '...', mobileAppId: '...' }}
  cacheTtlMs={10_800_000}
>
  <YourApp />
</ConsentProvider>

Use clearOfflineCache() from the package to wipe cached CDN assets (e.g. on logout or for debugging).


Checking Readiness

Use the isReady signal from useConsent to know when the SDK has finished loading settings, translations, and UI assets. Do not call consent APIs until isReady is true.

Your app UI inside ConsentProvider renders during initialization. Use isReady (and consent === null before native init completes) to avoid calling headless APIs too early.

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.


Getting 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.purposeMap      — Record<string, boolean>
// consent.purposes        — string[] of granted purpose keys
// consent.timestamp       — ISO 8601 string (optional)

Set 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.

Headless setConsent, acceptAll, and rejectAll do not close the bundled consent UI. UI buttons (Accept All, Reject All, Save) close the banner/modal after a successful mutation. To close programmatically after a headless update, call hideConsentManager() from useConsentUI.


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 — without it, 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:

  1. During initialization using the token from config.
  2. After setConsent when autoSync is true (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).


Consent UI (useConsentUI)

UI orchestration APIs live on useConsentUI, not useConsent:

import { useConsent, useConsentUI } from '@transcend-io/cm-mobile-ui-react-native';

const { isReady } = useConsent();
const {
  showConsentManager,
  hideConsentManager,
  autoShowConsentManager,
  openPreferences,
} = useConsentUI();

if (!isReady) return null;

showConsentManager(); // Opens default experience
showConsentManager({ viewState: 'banner-into-modal' }); // Opens a specific variant

Teardown (destroy)

To tear down the native SDK (e.g. logout, before re-initialization), call destroy() from useConsent. This is a heavy operation — it is not invoked automatically when ConsentProvider unmounts.

const { destroy } = useConsent();

await destroy(); // Closes UI, calls native TranscendAPI.destroy(), resets JS state

After destroy(), remount ConsentProvider when you need a clean re-init. With destroyOnClose: false (default), the native SDK persists across provider remounts until you call destroy().


Additional headless APIs

const { acceptAll, rejectAll } = useConsent();

await acceptAll(); // Does not close the consent UI
await rejectAll();

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 manage 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. | | Translation load failure | onError invoked with ERR_LOAD_TRANSLATIONS; isReady stays false. | | API called before ready | Headless APIs return safe defaults or false; gate on isReady for real data. | | useConsent outside ConsentProvider | Throws: "useConsent must be used inside ConsentProvider". | | useConsentUI outside provider | Throws: "useConsentUI must be used inside ConsentProvider". |