All docs
Feature flagsSDKsReactcode

React

Flags in the browser, bootstrapped for the first paint and updating live.

What you'll have

A page whose flags change without a reload, and no flash of the wrong branch.

You'll need
  • A client-side ID from Settings → Project

Flags in the browser, updating on an open page without a reload.

npm install @varia-bly/react

Use the client-side ID

import { VariablyProvider, useFlags } from '@varia-bly/react';

export function App({ children, bootstrappedFlags }) {
  return (
    <VariablyProvider
      clientSideId={process.env.NEXT_PUBLIC_VARIABLY_CLIENT_ID}
      options={{ baseUri: process.env.NEXT_PUBLIC_VARIABLY_BASE_URL }}
      flags={bootstrappedFlags}
    >
      {children}
    </VariablyProvider>
  );
}

Not the API key. This runs where anyone can read the bundle. The client-side ID is publishable and reaches flag evaluation only; the API key also reaches prompts, dynamic configs and LLM execution. Find it under Settings → Project.

With a client-side ID the provider evaluates on mount and holds a socket open, so a flag toggled in the dashboard reaches this page in about a second.

Reading flags

function Checkout() {
  const { checkoutRedesign } = useFlags();

  // Three states, and they differ. undefined means "not resolved yet"; treating it as
  // false renders the off branch while values are still loading.
  if (checkoutRedesign === undefined) return <Spinner />;

  return checkoutRedesign ? <NewCheckout /> : <CurrentCheckout />;
}

Bootstrap, so the first paint is right

flags seeds the provider with values your server already resolved. Without it the first render happens before the browser has evaluated, and anything reading a flag flashes through undefined.

Resolve with the Node SDK on the server, pass the set as a page prop, and hand it to the provider. On a server-rendered page this is what stops the wrong branch appearing for a moment.

Per-user evaluation

useIdentify re-evaluates when the context changes — after a login, say:

useIdentify(user ? { key: user.id, custom: { plan: user.plan } } : null);

Also available: useVariablyClient() for the client itself, and withVariably(config) if you would rather wrap a component than nest a provider.

Without a credential

Given no clientSideId, the provider is bootstrap-only: it serves the flags handed to it and never calls variA/Bly. That suits a deployment whose key is server-side only — the server evaluates, passes the result down, and nothing reaches the browser. You lose live updates, not correctness.

A flag reading undefined here but fine on the server

The browser sees only gates available to client-side SDKs. Open the gate and check that Hide from client-side SDKs is unticked. A hidden flag is absent from the browser's set, which is indistinguishable from one that does not exist — deliberately, so a public credential cannot be used to list your flag names.