React SDK
The RocketFlag React SDK wraps the RocketFlag API in idiomatic React primitives — a provider, a hook, and a declarative component — so you get loading/error state and re-renders on resolution without hand-rolling effects.
It is built on browser-native fetch with zero runtime dependencies (React is a peer dependency).
Installation
Section titled “Installation”npm install @rocketflag/react-sdkRequires React 16.14 or newer.
Basic Usage
Section titled “Basic Usage”1. Wrap your app in the provider
Section titled “1. Wrap your app in the provider”Add RocketFlagProvider once, near the root of your app. It builds a single shared client (and shared cache) for every flag check below it.
import { RocketFlagProvider } from "@rocketflag/react-sdk";
function Root() { return ( <RocketFlagProvider cacheTtlSeconds={300}> <App /> </RocketFlagProvider> );}Provider props (all optional):
| Prop | Description |
|---|---|
version | API version segment (defaults to v1). |
apiUrl | Base API URL (defaults to https://api.rocketflag.app). |
cacheTtlSeconds | Default cache TTL in seconds, shared across all hooks. Omit to disable caching. |
cacheMaxEntries | Maximum number of cached flag responses (defaults to 10000). Evicts least recently used entry when full. |
client | Inject a pre-built client (testing / advanced use). |
2. Read a flag with the useFlag hook
Section titled “2. Read a flag with the useFlag hook”import { useFlag } from "@rocketflag/react-sdk";
function SignUpButton() { const { enabled, loading, error } = useFlag("IFldMzqP5jtv9wAL");
if (loading) return <Spinner />; if (error) return null; return enabled ? <NewSignUp /> : <OldSignUp />;}useFlag returns:
| Field | Type | Description |
|---|---|---|
enabled | boolean | flag?.enabled ?? false. |
flag | FlagStatus | null | The full flag, or null until resolved. |
loading | boolean | true until the first fetch settles. |
error | Error | null | The thrown error, if any. |
refetch | () => void | Re-run the fetch on demand. |
useFlagmust be called inside a<RocketFlagProvider>. Used outside one, it throwsuseFlag must be used within a <RocketFlagProvider>.
Use refetch to re-evaluate on demand — for example after a user action that may change targeting:
const { enabled, refetch } = useFlag("IFldMzqP5jtv9wAL");
return <button onClick={refetch}>Re-check feature</button>;3. Or render declaratively with <Flag>
Section titled “3. Or render declaratively with <Flag>”import { Flag } from "@rocketflag/react-sdk";
<Flag id="IFldMzqP5jtv9wAL" fallback={<OldBanner />} loading={<Spinner />}> <NewBanner /></Flag>;children render when the flag is enabled; fallback when it’s disabled or the fetch errors; loading while it’s pending. Both fallback and loading default to rendering nothing.
Advanced Usage
Section titled “Advanced Usage”Cohorts and environments
Section titled “Cohorts and environments”Pass a user context as the second argument. The cache keys on flag ID and context, so different cohorts/environments resolve independently.
const { enabled } = useFlag("IFldMzqP5jtv9wAL", { cohort: "beta", env: "staging" });cohort:string | number | boolean— cohort/variant identifier.env:string, letters, numbers, hyphens, and underscores (e.g.prod-portals,staging_v2). Required for Group Flags.targetingKey: a stable user identifier that makes percentage rollouts sticky. See Attributes and sticky rollouts.- any other key: an audience attribute, such as
planorcountry.
Input validation: the client validates its arguments and throws synchronously for bad input:
flagIdmust be a non-empty string, context values must bestring/number/boolean, andenvmust contain only alphanumeric characters, hyphens, or underscores. These surface via the hook’serrorfield.
Attributes and sticky rollouts
Section titled “Attributes and sticky rollouts”Add a targetingKey for sticky percentage rollouts, and any other keys as audience attributes, in the same context object:
const { enabled } = useFlag("IFldMzqP5jtv9wAL", { targetingKey: user.id, plan: "pro", country: "AU",});Percentage rollouts are sticky per key: the same targetingKey (or, without one, the same cohort) gets the same answer every time, so send a stable user identifier. Other keys are matched against the flag’s audience and are only read when the flag has one. Matching is exact and case-sensitive, and an attribute you do not send never matches. cohort, env and targetingKey are reserved and cannot be audience attributes. The SDK sends every key in the context to the API, so this needs no new method. See Sticky rollouts. With caching on, each distinct targetingKey is a separate cache entry.
In TypeScript, UserContext declares targetingKey, cohort, env, and accepts any other key as an audience attribute whose value is a ContextValue (string | number | boolean), so you no longer need a cast to use sticky percentage rollouts or audiences. Values cannot be undefined, so leave out any keys you do not have. Passing { plan: user.plan } where user.plan may be undefined is a compile error. TypeScript cannot see through an optional property on an object you have typed yourself, so that case throws at runtime instead. Because UserContext is defined with a string index signature, a context typed with a TypeScript interface will not implicitly type-check; declare custom context types using a type alias instead, or spread the object (useFlag(id, { ...context })).
Per-call cache override
Section titled “Per-call cache override”Pass call options as the third argument to override the provider default for a single call (or 0 to force a fresh fetch):
const { flag } = useFlag("IFldMzqP5jtv9wAL", {}, { ttlSeconds: 0 });Imperative use
Section titled “Imperative use”Need a client outside of React (e.g. in an event handler or loader)? Build one directly:
import { createRocketflagClient } from "@rocketflag/react-sdk";
const client = createRocketflagClient(); // (version?, apiUrl?, { ttlSeconds, maxEntries }?)const flag = await client.getFlag("IFldMzqP5jtv9wAL");Error Handling
Section titled “Error Handling”The underlying client throws the same three error types as the Node SDK:
APIError— the API returned a non-ok response (carriesstatusandstatusText).InvalidResponseError— the response wasn’t valid JSON or didn’t match the expected shape.NetworkError— a network failure, such as a dropped connection.
These surface via the error field on useFlag. You can narrow them:
import { APIError, InvalidResponseError, NetworkError } from "@rocketflag/react-sdk";
const { error } = useFlag("IFldMzqP5jtv9wAL");if (error instanceof APIError) { // error.status, error.statusText}Testing
Section titled “Testing”Inject a pre-built (or mock) client via the provider’s client prop. When set, version, apiUrl, cacheTtlSeconds, and cacheMaxEntries are ignored, so your components resolve flags without hitting the network:
const mockClient = { getFlag: async () => ({ id: "test", name: "Test Flag", enabled: true }),};
render( <RocketFlagProvider client={mockClient}> <SignUpButton /> </RocketFlagProvider>,);Server-Side Rendering (Next.js, Remix)
Section titled “Server-Side Rendering (Next.js, Remix)”Flag fetching runs client-side inside useEffect, so no request is made during SSR — the server render produces the loading state, and the real value resolves after hydration. This keeps the SDK safe to drop into server-rendered apps without leaking requests onto the server.
Caching notes & limitations
Section titled “Caching notes & limitations”- Caching is opt-in — without
cacheTtlSeconds(or a per-callttlSeconds), every check hits the API. - The cache holds at most 10,000 entries by default and evicts the least recently used entry when full. You can customize this limit using the
cacheMaxEntriesprovider prop or themaxEntriesclient cache option. - No in-flight de-duplication: two components requesting the same uncached flag in the same tick may each fire a request before the cache populates.
- The API is one-flag-per-request, so this SDK offers
useFlag(id)rather than a bulkuseFlags().
Types & Exports
Section titled “Types & Exports”The package is written in TypeScript and ships its own declarations. Everything you can import:
import { RocketFlagProvider, // provider component useFlag, // hook Flag, // declarative component createRocketflagClient, // imperative client factory APIError, // error classes NetworkError, InvalidResponseError,} from "@rocketflag/react-sdk";
import type { RocketFlagProviderProps, UseFlagResult, FlagProps, FlagStatus, UserContext, ContextValue, CacheOptions, CallOptions, RocketFlagClient,} from "@rocketflag/react-sdk";The resolved flag shape (FlagStatus) matches the API response:
type FlagStatus = { id: string; name: string; enabled: boolean;};