Skip to content

React Adapter

@useoptimus/react provides <FlagProvider>, useFlag, and useVariant.

Terminal window
npm install @useoptimus/react

Peer dependency: react@^18.0.0 || ^19.0.0 — the floor is 18 because useSyncExternalStore, which useFlag is built on, doesn’t exist in 17.

import { FlagProvider, useFlag } from "@useoptimus/react";
function App({ client, snapshot }) {
return (
<FlagProvider client={client} snapshot={snapshot}>
<Banner />
</FlagProvider>
);
}
function Banner() {
const flag = useFlag<boolean>("show-banner");
return flag.value ? <div>New banner!</div> : null;
}

client is a FlagsClient from @useoptimus/core, constructed and owned entirely by your app — <FlagProvider> never calls client.init()/client.dispose(). React StrictMode’s double-effect-invoke in dev would break a shared client on the second mount if it did, and a client is typically one-per-app anyway, not scoped to a component subtree.

Passing snapshot (a SerializedSnapshot from @useoptimus/node) puts the provider in snapshot mode: useFlag reads only from the hydrated snapshot, with zero calls to client.evaluate()/evaluateAll() — the concrete expression of the SSR contract: the server evaluates once, the client hydrates without re-evaluating.

The only way out of snapshot mode is an explicit, field-different context prop change:

<FlagProvider client={client} snapshot={snapshot} context={{ userId }}>

A re-render with a new object, same fields does not flip to live mode (an inline context={{}} literal is safe); a re-render with an actually different field does, calling client.setContext() once and switching every subsequent useFlag read to live client.evaluate() calls, subscribed to live updates via client.subscribe().

const variantKey = useVariant("checkout-v2"); // string | undefined

Returns undefined — never throws, never casts .value to a string — when the flag has no variantKey (e.g. it fell through to its default with no bucketing key resolvable). That’s a normal state, not a bug.

Terminal window
pnpm --filter @useoptimus/react test