SDKs

@flagbearer/react-native

The React Native SDK for Flagbearer. Requires React Native 0.72 or later.

Install

npm install @flagbearer/react-native

To keep flags between launches, also install a storage, such as AsyncStorage:

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

Keys

Use a client key, which starts with fbc_. Client keys are public and safe to include in app code. Server keys, which start with fbs_, are secret and belong only on your server, with @flagbearer/node.

Set up one client

Create one client for the whole app, 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,
});

With storage, the app starts with the flags each user last had, even offline. It's optional. Any store with getItem, setItem and removeItem works. For example, MMKV:

const storage = new MMKV();

export const flags = init({
  clientKey: "fbc_...",
  storage: {
    getItem: (key) => storage.getString(key) ?? null,
    setItem: (key, value) => storage.set(key, value),
    removeItem: (key) => storage.delete(key),
  },
});

Users

When the signed-in user is known or changes, call identify:

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

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

Checking a flag

Read flags with two small hooks:

// 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;
}

// False until the current user's flags have loaded.
export function useFlagsLoaded() {
  return useSyncExternalStore(
    flags.subscribe,
    () => flags.getStatus() !== "loading",
  );
}
const newCheckout = useFlag("new-checkout", false);
return newCheckout ? <NewCheckout /> : <Checkout />;

The default is used until the current user's flags have loaded, and when the flag doesn't exist. Use useFlagsLoaded to hold back UI that mustn't switch between the default and the real value:

const loaded = useFlagsLoaded();
const newCheckout = useFlag("new-checkout", false);
if (!loaded) return <ActivityIndicator />;
return newCheckout ? <NewCheckout /> : <Checkout />;

Typed flag keys

In TypeScript, the CLI can write a file listing the application's flags. Then a misspelled key is a type error, and editors autocomplete keys:

npx @flagbearer/cli generate types --app <app> --out src/flagbearer.d.ts
useFlag("new-checkout", false); // ok
useFlag("new-chekout", false); // type error: not a flag in this app

Commit the file, and run the command in its first line again after adding or removing a flag. Without the file, any string is a flag key.

Error reports

allFlags() returns every flag's current value, so error reports show exactly which flags were on. It doesn't count as checking them.

Sentry.setContext("flags", flags.allFlags());
// { "new-checkout": true, "dark-mode": false }