React
@flagward/react — provider, useFlag, and useFlags.
Installation
npm install @flagward/reactPublished as
flagward-sdk-reactup to 0.2.0. Same package, same API — it moved into the@flagwardscope so every SDK in this family shares one namespace the project actually owns.
Quick start
import { FlagwardProvider, useFlag } from '@flagward/react';
function App() {
return (
<FlagwardProvider apiKey="your-api-key">
<Dashboard />
</FlagwardProvider>
);
}
function Dashboard() {
const { value: showNewUI } = useFlag('show-new-ui');
return showNewUI ? <NewDashboard /> : <OldDashboard />;
}Hooks
Reach for useFlag. One flag, one decision, one hook — it is what most
components need:
const { value, isLoading, error } = useFlag('new-checkout');useFlags earns its place in three cases: the keys are not known where you
write the code (a debug panel, an admin view); you need a flag where a hook
cannot go (an event handler, a callback, a conditional branch — hooks cannot
be conditional, getFlag can); or a component reads several flags and one
call reads better than five.
const { flags, isLoading, error, getFlag } = useFlags();
flags; // { "new-checkout": true, ... }
getFlag('show-banner'); // one flag, the provider's context
getFlag('show-banner', { plan: 'pro' }); // one flag, plus this contextWhere context comes from
<FlagwardProvider context={{ plan: 'free' }}> // who the user is
useFlag('beta', { plan: 'pro' }) // just this callA context passed to useFlag belongs to that call only — it is not
published anywhere, and useFlags().flags resolves against the provider's
context alone. Put the user in the provider (plan, country, id, locale —
whatever your rules target), and reach for the per-call context when what
you are evaluating is not the current user, such as a row in a list:
users.map((u) => (
<Row key={u.id} badge={getFlag('premium-badge', { plan: u.plan })} />
));Provider
<FlagwardProvider
apiKey="your-api-key"
host="https://flags.example.com" // optional, defaults to https://app.flagward.com
context={{ userId: '123' }} // optional, used to evaluate targeting rules
logLevel="warn" // optional: "warn" | "error" | "silent"
>
{children}
</FlagwardProvider>Next.js
The App Router works with no wrapper of your own — the provider and hooks
declare "use client", so this imports straight into a layout:
// app/layout.tsx
import { FlagwardProvider } from '@flagward/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<FlagwardProvider apiKey={process.env.NEXT_PUBLIC_FLAGWARD_API_KEY!}>
{children}
</FlagwardProvider>
</body>
</html>
);
}In a server component, hooks do not run, but the client does — reach for
@flagward/core directly (already installed as a dependency of this
package):
// app/page.tsx
import { FlagwardClient } from '@flagward/core';
export default async function Page() {
const client = new FlagwardClient({ apiKey: process.env.FLAGWARD_API_KEY! });
await client.init();
return client.evaluate('new-checkout', { plan: 'pro' }) ? <NewCheckout /> : <LegacyCheckout />;
}FlagwardProvider starts its first read in an effect, which does not run on
the server: server-rendered HTML always carries isLoading: true, with a
flash of your fallback until the browser takes over. Passing a snapshot from
server to client is open work.
Losing the network
Flags are read once and evaluated locally, so a client that loses its connection keeps working — it just cannot notice a change until the network comes back or the tab is foregrounded again, at which point the SDK re-reads the flags.
Standalone client
import { FlagwardClient } from '@flagward/react';
const client = new FlagwardClient({ apiKey: 'your-api-key' });
await client.init();
client.cachedFlags; // configured on/off state, no targeting rules applied
client.evaluate('show-banner', { plan: 'premium' }); // targeting rules applied against this context
client.getFlag('maintenance-mode'); // same, without context, never throws
client.connect(); // keep the snapshot fresh via SSE
client.disconnect(); // close it when done