@flagbearer/react-native
The React Native SDK for Flagbearer. Requires React Native 0.72 or later.
Install
npm install @flagbearer/react-nativeTo keep flags between launches, also install a storage, such as AsyncStorage:
npm install @react-native-async-storage/async-storageKeys
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.tsuseFlag("new-checkout", false); // ok
useFlag("new-chekout", false); // type error: not a flag in this appCommit 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 }