Skip to content
Consent Management
API & SDK Reference
React Native SDK

Add consent management to a React Native app

30minAstralisMobile Consent
This guide covers the Janus React Native SDK. For native iOS, Android and Flutter apps, see the Mobile SDK Reference.

The Janus React Native SDK, @ethyca/janus-sdk-react-native, brings Astralis consent management to React Native apps. It shows your Astralis privacy experiences in the app, stores the user's choices, and saves them to Astralis, through the same Janus SDKs that native iOS and Android apps use.

This guide walks through installing the SDK, initializing it, showing the privacy experience and reading consent. It then covers the optional features, and how to ship SDK updates to users.

How it works

The SDK is a React Native TurboModule that wraps the native Janus SDKs for iOS and Android:

  1. The native SDKs do the work. They fetch the experience for the user's region, show it in a native view, store consent on the device, and save it to Astralis. The React Native layer exposes them through the Janus object.
  2. The API follows the other Janus SDKs. Most calls have the same names and behaviour as on iOS, Android and Flutter, so the Mobile SDK Reference applies. In React Native, getters such as Janus.consent return a promise, and web views use the useJanusWebView hook instead of createConsentWebView. The API reference below lists every call.
  3. Consent reaches the rest of the app. The native SDKs write the IAB TCF keys that ad SDKs read, and useJanusWebView gives web pages the same consent. See Show web pages with the app's consent in React Native.

Requirements

RequirementMinimum version
React Native0.81, with the New Architecture. The legacy bridge isn't supported.
React19.1
iOS15.1
Xcode27
AndroidAPI 24
Expo (optional)SDK 54, with expo prebuild
Node.js20.19.4

Step 1: Install the SDK

Releases are published as tags on the ethyca/janus-sdk-react-native (opens in a new tab) repository rather than to the npm registry. Install a release as a git dependency, replacing <version> with a release tag. Tags are plain version numbers, without a v prefix:

npm install github:ethyca/janus-sdk-react-native#<version>

Each tag holds the built package, so installing it doesn't run a build.

Step 2: Set up the iOS and Android projects

The SDK depends on the native Janus SDKs, which are published outside CocoaPods trunk and Maven Central. Your app needs to point its build tools at them.

Expo apps

Apps built with expo prebuild can use the SDK's config plugin. Add it to app.json, then run npx expo prebuild:

{
  "expo": {
    "plugins": ["@ethyca/janus-sdk-react-native"]
  }
}

The plugin adds the iOS podspec line and the Android Maven repository described below, and keeps them in place across later prebuilds. The iOS pod includes the SDK's privacy manifest, so there's nothing to add for it.

The plugin's userTrackingPermission option sets the iOS tracking message, as described in App Tracking Transparency. If your app already uses expo-tracking-transparency, keep setting the message there and leave this option out.

Bare React Native apps

iOS: add the JanusSDK podspec to your ios/Podfile, then run pod install. Without it, pod install fails with an error like this one:

[!] Unable to find a specification for `JanusSDK (= 1.2.0)` depended upon by `JanusSdkReactNative`

The SDK pins an exact native version, so read it from the installed package rather than copying the number:

require 'json'
 
janus_ios_version = JSON.parse(File.read(Pod::Executable.execute_command('node', ['-p',
  'require.resolve("@ethyca/janus-sdk-react-native/package.json", {paths: [process.argv[1]]})',
  __dir__]).strip))['janusNative']['ios']
 
target 'YourApp' do
  config = use_native_modules!
 
  pod 'JanusSDK', :podspec => "https://raw.githubusercontent.com/ethyca/janus-sdk-ios/#{janus_ios_version}/JanusSDK.podspec"
 
  # ...
end

Android: add Ethyca's Maven repository to android/build.gradle, or under dependencyResolutionManagement in android/settings.gradle if your app declares repositories there:

allprojects {
  repositories {
    maven {
      url "https://ethyca.github.io/janus-sdk-android"
      content { includeGroup "com.ethyca.janussdk" }
    }
  }
}

The content filter limits the repository to Ethyca's artifacts. Rebuild the app on both platforms after setup.

Each release of the React Native SDK pins the native SDK versions it was tested with, in the janusNative field of its package.json. The iOS pin is exact. The Android pin is a minimum: if your app also depends on a newer com.ethyca.janussdk:android, Gradle uses that one.

Step 3: Initialize Janus and show the privacy experience

Initialize Janus early in your app's lifecycle, with your Astralis API host and the property ID of your app. Your Ethyca account manager can provide both.

import { Janus, JanusError } from "@ethyca/janus-sdk-react-native";
 
try {
  await Janus.initialize({
    apiHost: "https://privacy.example.com",
    propertyId: "FDS-XXXXXX",
  });
} catch (error) {
  if (error instanceof JanusError) {
    console.warn(error.code, error.message);
  }
}

By default, Janus shows the experience for the user's region as soon as it initializes. To choose the moment yourself, set autoShowExperience: false and show it when consent is still needed:

await Janus.initialize({ apiHost, propertyId, autoShowExperience: false });
if (await Janus.shouldShowExperience) {
  await Janus.showExperience();
}

To let users change their choices later, for example from a "Privacy settings" item, call Janus.showExperience().

Configuration

Only apiHost is required.

OptionDefaultEffect
apiHostRequiredThe base URL of the Astralis API.
propertyId""The property ID of your app.
privacyCenterHost""The Privacy Center host. When empty, Janus uses apiHost.
ipLocationtrueLooks up the user's region from their IP address.
region""The region to use when IP location is off or fails, such as "US-CA".
autoShowExperiencetrueShows the experience automatically after initialize.
fidesEventstrueMaps Janus events to FidesJS events in web views.
saveUserPreferencesToFidestrueSaves the user's choices to Astralis.
saveNoticesServedToFidestrueRecords which notices were served.
consentFlagType"boolean"The format of consent values: "boolean", or "consentMechanism" for strings such as "opt_in".
consentNonApplicableFlagMode"omit"Whether consent includes notices that don't apply in the user's region: "omit" or "include".
enableBannerModefalseShows banner_and_modal experiences as a banner first. See Banner mode.
enableAttfalseiOS only. Asks for App Tracking Transparency before the experience. See App Tracking Transparency.

Errors

Every call that returns a promise rejects with a JanusError, whose code is one of these values:

CodeWhen
NOT_LINKEDThe native module isn't in the app. Rebuild it after installing, with pod install on iOS.
INVALID_ARGSA call had missing or invalid arguments.
INIT_ERRORinitialize failed.
NO_PRESENTERshowExperience found no view controller (iOS) or activity (Android) to show from.
SHOW_EXPERIENCE_ERRORShowing the experience failed.
LOCATION_ERRORgetLocationByIPAddress failed.
UNEXPECTED_ERRORAny other native failure. The original error is in cause.

Step 4: Read consent and listen for changes

Read the user's stored consent at any time after initialize:

const consent = await Janus.consent; // notice keys to values, such as { analytics: false }
const fidesString = await Janus.fidesString; // the TC string after a TCF choice

To act when the user makes a choice, for example to start or stop SDKs that depend on consent, subscribe to consent events. The experienceSelectionUpdated event carries the stored consent and fidesString in its detail, so the listener doesn't need a separate read:

import {
  Janus,
  JanusEventType,
  consoleLogger,
} from "@ethyca/janus-sdk-react-native";
 
// While you develop, send the native SDKs' log lines to the console.
await Janus.setLogger(consoleLogger);
 
const subscription = Janus.addConsentEventListener((event) => {
  if (event.eventType === JanusEventType.experienceSelectionUpdated) {
    const { consent, fidesString } = event.detail ?? {};
    // Apply the user's choice.
  }
});
 
// When you no longer need it:
subscription.remove();

The native SDKs only forward events while a listener exists, so an app that doesn't listen pays nothing for them. Event types the installed SDK version doesn't know arrive as unknown. Janus.setLogger(null) stops the log forwarding.

Step 5: Verify the integration

  1. Run the app on an iOS device or simulator and on an Android device or emulator.
  2. Confirm the privacy experience for your region shows, either automatically or when you call showExperience.
  3. Make a choice in the experience. Your listener receives experienceSelectionUpdated with the new consent.
  4. Restart the app. Janus.shouldShowExperience is now false, and Janus.consent returns the saved choice.

Optional features

Banner mode

By default, Janus shows every non-TCF experience as a full-screen modal. Set enableBannerMode: true to show banner_and_modal experiences as a compact banner first, as on the web. The banner offers Reject All, Accept All, and Manage preferences, which opens the full modal. The banner only appears while consent is still needed. Dismissing it saves the notices' defaults as implied consent, which doesn't count as a choice, so the banner shows again on the next showExperience.

App Tracking Transparency (iOS)

Set enableAtt: true to have Janus ask for Apple's tracking permission before it shows the experience. If the user denies tracking, notices that depend on it are set to opt out and locked, as described in the Apple ATT guide. Your app needs NSUserTrackingUsageDescription in Info.plist. In an Expo app, set it with the config plugin's userTrackingPermission option:

[
  "@ethyca/janus-sdk-react-native",
  { "userTrackingPermission": "We use this to personalize ads." }
]

If your app shows the prompt itself today, for example with requestTrackingPermissionsAsync from expo-tracking-transparency, stop calling it and set enableAtt: true. iOS only shows the prompt once per install, so existing users aren't asked again: the first time Janus shows the experience, it reads the answer they already gave and locks the same notices as for a new user who denies tracking.

Carry over an earlier "Do Not Sell" choice

If your app recorded a "Do Not Sell" opt-out in the IAB US Privacy string before it used Janus, Janus.carryOverUSPrivacyOptOut() keeps those users opted out. Call it after initialize and before the experience shows, with autoShowExperience: false. It opts the user out of the data_sales_and_sharing notice and saves that to Astralis with the external_provider consent method. Pass noticeKey to use a different notice, or usPrivacyString if your app stored the string under another key. Stored Janus consent always wins, so it's safe to call on every launch.

The call resolves with what it did: optedOut, consentExists (Janus already had a value), noExperience (no experience loaded; the next launch tries again), notOptedOut, or noUSPrivacyString.

Web pages and ad SDKs

To show web pages that run FidesJS with the app's consent, and to give native ad SDKs and in-page ad tags the same consent, see Show web pages with the app's consent in React Native.

Ship SDK updates to users

Each release's entry in the SDK's CHANGELOG.md says whether it needs a new native build:

  • Native build required: yes: the release changes native code, the Expo config plugin, the pinned native SDK versions, or native dependencies. Ship it in a new App Store and Google Play build.
  • Native build required: no: the release only changes JavaScript. If your app uses an over-the-air update service such as EAS Update, you can ship it with an update, without a store release.

An over-the-air update can't change native code. If you ship a yes release over the air, its JavaScript runs against the old native module, which it may not work with.

API reference

CallDescription
initialize(config)Configures Janus and loads the experience for the user's region. With a page shown through useJanusWebView on Android, it resolves once that page's cookie is rewritten, which waits for a cookie write already in progress.
showExperience()Shows the privacy experience.
shouldShowExperienceWhether the experience should be shown, because consent is missing or out of date.
hasExperienceWhether an experience exists for the user's region and the property.
currentExperienceThe current experience, or null.
isTCFExperienceWhether the current experience is a TCF experience.
regionThe region Janus is using.
consentThe user's consent, as notice keys to values.
internalConsentThe same values as consent.
consentMetadataWhen consent was created and updated (as Date objects, or null), how it was given, and the experience version hash.
fidesStringThe user's consent strings, such as the TC string, combined in one value.
webViewConsentConsent, fides string, metadata and fides_user_device_id in one read, for seeding a web view. Call it after initialize resolves; before that, Android rejects.
setConsent(options)Sets consent from outside the experience, optionally saving it to Astralis.
clearConsent(options)Clears consent, and its metadata when clearMetadata is true. With a page shown through useJanusWebView on Android, it resolves once that page's cookie is cleared, which waits for a cookie write already in progress.
setExternalId(id)Links your app's user ID to later consent records. Pass null on logout.
getIABTCFValues()The IAB TCF values stored on the device.
getUSPrivacyString()The IAB US Privacy string stored on the device, or null.
carryOverUSPrivacyOptOut(options)Carries an earlier "Do Not Sell" opt-out into Janus consent.
getLocationByIPAddress()Looks up the user's region from their IP address.
addConsentEventListener(listener)Calls listener with each consent event, until you call remove() on the returned subscription.
removeConsentEventListener(subscription)Removes a listener.
setLogger(logger)Sends the native SDKs' log lines to a JavaScript logger, such as consoleLogger. Pass null to stop.
useJanusWebView(options)A React hook that shows a web page with the app's consent. See React Native web views.

Related