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:

PropTypeRequiredDescription
projectKeystringYesYour project key
publicKeystringYesPublishable API key
secretKeystringNoSecret API key (server-side only)
options.realtimebooleanNoEnable real-time SSE updates
options.staleTimeMsnumberNoCache stale threshold in ms
options.apiUrlstringNoCustom API base URL
options.userFlagifyUserNoUser 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:

  1. After your auth layer loads the user, mount <FlagifyProvider> with options.user.
  2. 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.
  3. 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 | undefinedundefined 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>