Quickstart by stack
React Quickstart
Install the React SDK, wrap your app in FlagsProvider, and read flags with useFlag, useVariant, and FlagGate.
Prerequisites
- React 18.2 or newer (React 19 works too) and @zenmanage/sdk 3.5 or newer.
- A client key (prefixed cli_) stored in build-time configuration. Server and mobile keys are not valid for the browser.
- A flag key you can test in a non-production environment.
npm install @zenmanage/react @zenmanage/sdk
import { FlagsProvider, useFlag } from '@zenmanage/react';
function CheckoutButton() {
const { value: enabled, isLoading } = useFlag('new-checkout-button', false);
if (isLoading) {
return <button disabled>Loading...</button>;
}
return <button>{enabled ? 'Checkout (New)' : 'Checkout'}</button>;
}
export function App() {
return (
<FlagsProvider environmentToken={import.meta.env.VITE_ZENMANAGE_CLIENT_KEY}>
<CheckoutButton />
</FlagsProvider>
);
}
Safe defaults, explicit loading
Every hook takes a default and returns it until the flag resolves, and again if evaluation fails. isLoading and error are explicit, so you decide what the UI does in each state. The type of the default decides how the flag is read: boolean, string, number, or JSON.
Built on the JavaScript SDK
The package is a thin wrapper around @zenmanage/sdk, so caching, context handling, and defaults behave the same way they do in the JavaScript quickstart. Pass your own client to the provider if you already have one.
import { Attribute, Context } from '@zenmanage/sdk';
import { FlagsProvider, useFlag } from '@zenmanage/react';
function PremiumBanner() {
const { value } = useFlag('premium-banner', false);
return value ? <div>Premium Banner</div> : null;
}
export function App({ user }: { user: { id: string; name: string; plan: string } }) {
const context = new Context('user', user.name, user.id, [new Attribute('plan', [user.plan])]);
return (
<FlagsProvider environmentToken={import.meta.env.VITE_ZENMANAGE_CLIENT_KEY} context={context}>
<PremiumBanner />
</FlagsProvider>
);
}
import { useVariant } from '@zenmanage/react';
function CheckoutExperience() {
const { variant, isLoading } = useVariant('checkout-flow', 'control');
if (isLoading) {
return <p>Loading checkout...</p>;
}
if (variant === 'one-page') {
return <OnePageCheckout />;
}
return <MultiPageCheckout />;
}
import { FlagGate } from '@zenmanage/react';
<FlagGate flagKey="beta-chat" disabledFallback={<LegacyChat />}>
<BetaChat />
</FlagGate>
const { value: ui } = useFlag('ui-config', { theme: 'light', pageSize: 20 });
const { value: steps } = useFlag<string[]>('onboarding-steps', []);
Client keys only in the browser
Use a cli_ key in the browser. A srv_ key throws there, and a cli_ key throws in Node.js. Never ship a server key to the browser — read it from a server-only environment variable.
Server-side rendering
Effects don't run on the server, so hooks render their defaults with isLoading set to true and flags load after hydration. If you need real values in the first paint, fetch them on the server with @zenmanage/sdk and pass them down as props.
Next step
Take the next integration step in your own stack.
Start with the quickstart that matches your runtime, then return to the reference pages when you need exact request and payload details.
Language and framework names and logos shown above are trademarks or registered trademarks of their respective owners. Their use here is for identification purposes only and does not imply endorsement of, or affiliation with, Zenmanage.