Quickstart by stack

React Native Quickstart

Install the React Native SDK, wrap your app in FlagsProvider, and read flags with useFlag, useVariant, and FlagGate.

Prerequisites

  • React Native 0.72 or newer, or an Expo app. React 18.2 or newer (React 19 works too).
  • A mobile key (prefixed mob_) or a client key (prefixed cli_) from your environment. Server keys (srv_) are rejected, because anything bundled in an app can be extracted.
  • A flag key you can test in a non-production environment.
Install bash

npm install @zenmanage/react-native @zenmanage/sdk react react-native

For rules that survive an app restart, add the AsyncStorage package too. It is optional, but recommended. Without it, the SDK keeps rules in memory only.

Install the optional persistent cache bash

npm install @react-native-async-storage/async-storage
Wrap your app and read a flag tsx

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { FlagsProvider, useFlag } from '@zenmanage/react-native';

const styles = StyleSheet.create({
  wrapper: { padding: 24 },
});

function CheckoutLabel() {
  const { value: enabled, isLoading } = useFlag('new-checkout', false);

  if (isLoading) {
    return <Text>Loading flags...</Text>;
  }

  return <Text>{enabled ? 'Checkout (New)' : 'Checkout'}</Text>;
}

export function App() {
  return (
    <FlagsProvider environmentToken="mob_your_mobile_key_here">
      <View style={styles.wrapper}>
        <CheckoutLabel />
      </View>
    </FlagsProvider>
  );
}

Safe defaults, explicit loading

Every hook takes a default and returns it until the flag resolves, and again if evaluation fails. isLoading and error are explicit, so you decide what the screen shows in each state. The type of the default decides how the flag is read: boolean, string, number, or JSON.

Built on the core SDK

The package wraps @zenmanage/sdk, so caching, context handling, and defaults behave the same way they do in the JavaScript quickstart. Pass your own client to the provider if you already have one.

Target a user with identify tsx

import { Context } from '@zenmanage/sdk';
import { useIdentify } from '@zenmanage/react-native';

export function useAccountTargeting() {
  const { identify, clearIdentity } = useIdentify();

  return {
    // Refreshes the rules for this user by default.
    onSignIn: (userId: string) => identify(Context.single('user', userId)),
    // Drops the user context and refreshes again.
    onSignOut: () => clearIdentity(),
  };
}

Call identify at login and clearIdentity at logout. Use a stable identifier, such as your user ID, so a percentage rollout keeps the same users in the same bucket across launches.

Run an A/B test with a variant tsx

import { useVariant } from '@zenmanage/react-native';

function CheckoutScreen() {
  const { variant } = useVariant('checkout-flow', 'control');

  if (variant === 'one-page') {
    return <OnePageCheckout />;
  }

  return <MultiPageCheckout />;
}
Gate a screen with FlagGate tsx

import { FlagGate } from '@zenmanage/react-native';

<FlagGate
  flagKey="beta-chat"
  loadingFallback={<Spinner />}
  disabledFallback={<LegacyChat />}
>
  <BetaChat />
</FlagGate>

Background and foreground refreshes never show loadingFallback. A gated screen stays mounted while rules re-fetch, and its value updates once the new rules land.

Persist rules across launches tsx

import AsyncStorage from '@react-native-async-storage/async-storage';
import { AsyncStorageCache, FlagsProvider } from '@zenmanage/react-native';

const cache = new AsyncStorageCache({
  storage: AsyncStorage,
  keyPrefix: 'zenmanage',
});

export function App() {
  return (
    <FlagsProvider environmentToken="mob_your_mobile_key_here" cache={cache} cacheTtl={3600}>
      <Root />
    </FlagsProvider>
  );
}

Refresh with the app lifecycle

The provider refreshes rules when the app returns to the foreground, at most once per minForegroundRefreshIntervalMs (30 seconds by default). Set backgroundRefreshIntervalMs if you also want periodic refreshes while the app is open.

Mobile or client keys only

Use a mob_ or cli_ key in the app. Keys in an app bundle are readable by anyone with the binary, so keep srv_ keys on your backend.

Next step

Take the next integration step in your own stack.

Start with the quickstart that matches your runtime, then return to the reference pages when you need exact request and payload details.

Language and framework names and logos shown above are trademarks or registered trademarks of their respective owners. Their use here is for identification purposes only and does not imply endorsement of, or affiliation with, Zenmanage.