Flagbearer

Feature flags in React Native

A feature flag lets you put a new screen in a release now and decide later who sees it, without waiting on another build or store review. This guide adds flags to a React Native app with one small module, and covers the parts that matter on mobile: starting up offline and signed-in users.

The examples use Flagbearer's React Native SDK. The React Native docs have the full reference, and there's a complete Expo app to copy at flagbearer-dev/react-native-example.

Install

npm install @flagbearer/react-native @react-native-async-storage/async-storage

The SDK needs React Native 0.72 or later. AsyncStorage is optional. It's what lets flags work offline, as the next section shows.

One client, with storage

Create the client once, in its own module:

// src/flags.ts
import AsyncStorage from "@react-native-async-storage/async-storage";

import { init } from "@flagbearer/react-native";

export const flags = init({
  clientKey: "fbc_...",
  storage: AsyncStorage,
});

Client keys start with fbc_ and are public, so they're safe in app code.

With storage, the app starts with the flags each user last had, even with no connection. Any store with getItem, setItem and removeItem works, so you can use MMKV in place of AsyncStorage.

A useFlag hook

Read flags with React's useSyncExternalStore. That way a screen re-renders when a flag changes:

// src/flags.ts
import { useEffect, useSyncExternalStore } from "react";

import type { FlagKey } from "@flagbearer/react-native";

export function useFlag(key: FlagKey, defaultValue: boolean) {
  const enabled = useSyncExternalStore(
    flags.subscribe,
    () => flags.getSnapshot()[key] ?? defaultValue,
  );

  useEffect(() => {
    flags.isEnabled(key, defaultValue);
  }, [key, defaultValue, enabled]);

  return enabled;
}

Check a flag on a screen

function CheckoutScreen() {
  const newCheckout = useFlag("new-checkout", false);
  return newCheckout ? <NewCheckout /> : <Checkout />;
}

The second argument is the default: what the screen shows until flags have loaded. When you turn the flag on in Flagbearer, the app picks it up within seconds, with no new build.

Tell the client who is signed in

Rollouts work per user, so tell the client who's signed in as soon as you know:

await flags.identify({ key: user.id, email: user.email });

The email is optional. Include it to turn flags on for your testers by email.

No flash of the wrong screen

Some screens shouldn't switch from the old version to the new one while the user is looking. Add a second hook that says whether flags have loaded, and hold those screens back until it's true:

// src/flags.ts
export function useFlagsLoaded() {
  return useSyncExternalStore(
    flags.subscribe,
    () => flags.getStatus() !== "loading",
  );
}
const loaded = useFlagsLoaded();
const newCheckout = useFlag("new-checkout", false);
if (!loaded) return <ActivityIndicator />;
return newCheckout ? <NewCheckout /> : <Checkout />;

Typed flag keys

In TypeScript, one command writes a file listing your app's flags. After that, a misspelled key is a type error and your editor autocompletes them:

npx @flagbearer/cli generate types --app <app> --out src/flagbearer.d.ts

Next: roll it out

With the flag in place, the feature can go to your team first and then to everyone, a step at a time. See how to roll a feature out gradually.

Start freeRead the docs