React SDK
The @flagify/react package provides React hooks and a context provider for evaluating feature flags in React applications.
Installation
npm install @flagify/react
Setup
Wrap your application with the FlagifyProvider:
import { FlagifyProvider } from '@flagify/react';
function App() {
return (
<FlagifyProvider
projectKey="my-project"
publicKey="pk_dev_abc123_xxxxxxxx"
options={{ realtime: true }}
>
<YourApp />
</FlagifyProvider>
);
}
Provider props
The provider accepts all FlagifyOptions from @flagify/node plus children:
| Prop | Type | Required | Description |
|---|---|---|---|
projectKey | string | Yes | Your project key |
publicKey | string | Yes | Publishable API key |
secretKey | string | No | Secret API key (server-side only) |
options.realtime | boolean | No | Enable real-time SSE updates |
options.staleTimeMs | number | No | Cache stale threshold in ms |
options.apiUrl | string | No | Custom API base URL |
options.user | FlagifyUser | No | User context for targeting (see below) |
User context & targeting
Targeting rules let a flag return different values per user — for example, an admin-tools flag that’s only true for users whose role === 'admin', or a beta-features flag enabled for plan === 'enterprise'. Targeting rules are configured server-side (Flagify dashboard or API); the React SDK only forwards user attributes.
The pattern is one-shot, not per-flag:
- After your auth layer loads the user, mount
<FlagifyProvider>withoptions.user. - The Provider’s underlying client fetches all flag values already evaluated against the targeting rules for that user and stores them in its local cache.
useFlag('admin-tools')reads the cached, already-targeted value.
import { FlagifyProvider, useFlag } from '@flagify/react';
import { useCurrentUser } from './auth';
function Root() {
const user = useCurrentUser();
return (
<FlagifyProvider
key={user?.id ?? 'anonymous'}
projectKey="my-project"
publicKey="pk_dev_abc123_xxxxxxxx"
options={{
realtime: true,
user: user
? { id: user.id, role: user.role, email: user.email }
: undefined,
}}
>
<App />
</FlagifyProvider>
);
}
function AdminMenu() {
// useFlag returns `undefined` until the initial sync completes, so compare
// explicitly — don't assume truthiness.
const canSeeAdmin = useFlag('admin-tools');
if (canSeeAdmin !== true) return null;
return <Admin />;
}
Where to mount the Provider
Mount <FlagifyProvider> below the provider that loads your user, so the user is available when the Flagify client initializes. If the Provider mounts before the user is known, the cache is populated with the anonymous evaluations — catch-all and rollout rules still apply correctly, but any rule that targets by user attributes will miss until the Provider remounts with the real user (use key={user?.id ?? 'anonymous'} to force that resync on login/logout).
When the user changes (login / logout)
The Provider re-syncs flags when options.user.id changes. The simplest reliable pattern is to remount the Provider with key={user?.id ?? 'anonymous'} — switching from anonymous to a real id, or between two real ids, tears down the old client and creates a fresh one with the new user.
User object shape
{
id: string // required — the user identifier (NOT "userId")
email?: string
role?: string
group?: string
geolocation?: { country?: string; region?: string; city?: string }
[key: string]: unknown // custom attributes (plan, betaCohort, ...)
}
The field is id, not userId. The SDK serializes it to userId on the wire automatically.
For server-side per-request evaluation (Next.js API routes, Express, etc.), use flagify.evaluate(key, user) from @flagify/node directly. See the targeting concept docs and the JavaScript SDK reference.
Hooks
useFlag
Evaluate a boolean flag. Returns boolean | undefined — undefined while the client is still syncing (isReady === false), then true/false once the cache is populated. When the flag is disabled, returns its offValue. Returns false if the flag doesn’t exist.
Because the first render can be undefined, gate on an explicit comparison (or use useIsReady) instead of relying on truthiness — especially for flags whose “off” state is visible UI.
import { useFlag } from '@flagify/react';
function Navbar() {
const showNewNav = useFlag('new-navbar');
// Wait for sync before deciding — avoids a flash of the classic navbar.
if (showNewNav === undefined) return <NavbarSkeleton />;
return showNewNav ? <NewNavbar /> : <ClassicNavbar />;
}
useVariant
Get the winning variant key for a multivariate flag. Returns the variant with the highest weight:
import { useVariant } from '@flagify/react';
function Checkout() {
const variant = useVariant('checkout-flow', 'classic');
switch (variant) {
case 'streamlined':
return <StreamlinedCheckout />;
case 'classic':
default:
return <ClassicCheckout />;
}
}
useFlagValue
Get a typed value from a flag. When disabled, returns the server’s offValue. The fallback is used only when the flag doesn’t exist in cache:
import { useFlagValue } from '@flagify/react';
function Banner() {
const message = useFlagValue<string>('banner-message', 'Welcome!');
return <div className="banner">{message}</div>;
}
const config = useFlagValue<{ maxItems: number }>('cart-config', { maxItems: 50 });
useIsReady
Check if the initial flag sync is complete:
import { useIsReady, useFlag } from '@flagify/react';
function Feature() {
const isReady = useIsReady();
const enabled = useFlag('new-feature');
if (!isReady) return <Skeleton />;
return enabled ? <NewFeature /> : <OldFeature />;
}
useFlagifyClient
Access the underlying Flagify client directly:
import { useFlagifyClient } from '@flagify/react';
function DebugPanel() {
const client = useFlagifyClient();
// Use client.isEnabled(), client.getValue(), etc.
}
Real-time updates
When realtime: true is set on the provider, all hooks automatically re-render when flags change via SSE. No additional configuration needed.
<FlagifyProvider
projectKey="my-project"
publicKey="pk_dev_abc123_xxxxxxxx"
options={{ realtime: true }}
>
{/* Components using useFlag will re-render when flags change */}
<YourApp />
</FlagifyProvider>