@flagskit/react
v0.3.0
Published
React bindings for FlagsKit — Provider, hooks, and components.
Maintainers
Readme
React bindings for FlagsKit — Provider, hooks, and components. Percentage rollout, user targeting, type-safe. No backend required.
pnpm add @flagskit/react
# or
npm install @flagskit/reactQuickstart
1. Define your flags once
// flags.ts
import { createFlagKit, defineFlags } from '@flagskit/react'
type AppFlags = {
'new-checkout': boolean
'pricing-model': 'legacy' | 'v2' | 'v3'
'max-upload-mb': number
}
export const { FlagProvider, useFlag, useFlags, Feature, Variant } =
createFlagKit<AppFlags>(
defineFlags<AppFlags>({
'new-checkout': {
defaultValue: false,
rules: [
{ match: { role: 'beta' }, value: true },
{ percentage: 20, value: true },
],
},
'pricing-model': {
defaultValue: 'legacy',
rules: [{ match: { country: 'US' }, value: 'v2' }],
},
'max-upload-mb': {
defaultValue: 10,
rules: [{ match: { plan: 'pro' }, value: 100 }],
},
}),
)2. Wrap your app
// App.tsx
import { FlagProvider } from './flags'
function App() {
return (
<FlagProvider context={{ userId: user.id, role: user.role, plan: user.plan }}>
<Router />
</FlagProvider>
)
}3. Use anywhere — fully typed
import { useFlag, Feature, Variant } from './flags'
// Hook — return type inferred from your schema
const isNew = useFlag('new-checkout') // boolean
const model = useFlag('pricing-model') // 'legacy' | 'v2' | 'v3'
// Boolean flag — show/hide
<Feature flag="new-checkout" fallback={<OldCheckout />}>
<NewCheckout />
</Feature>
// Render prop — access the value
<Feature flag="max-upload-mb">
{(maxMB) => <FileUpload limit={maxMB} />}
</Feature>
// Multivariate — render per value
<Variant
flag="pricing-model"
variants={{ legacy: <LegacyPricing />, v2: <PricingV2 />, v3: <PricingV3 /> }}
/>API
createFlagKit<T>(config)
Primary API. Creates a fully typed set of React bindings bound to your flag schema. Import from './flags' in your app — never import hooks directly from @flagskit/react.
Returns: { FlagProvider, useFlag, useFlags, Feature, Variant }
defineFlags<T>(config)
Type-safe config factory. Validates flag definitions against your schema at compile time.
<FlagProvider context? adapter?>
Evaluates all flags and provides values to the component tree. Re-evaluates when context or adapter changes.
| Prop | Type | Description |
|---|---|---|
| context | FlagContext | User/environment data for rule matching |
| adapter | FlagAdapter<T> | Optional override source |
type FlagContext = {
userId?: string // required for percentage rollout
env?: string
[key: string]: string | number | boolean | undefined
}useFlag(flagName)
Returns the evaluated value of a single flag. Return type inferred from your schema.
const isNew = useFlag('new-checkout') // boolean
const model = useFlag('pricing-model') // 'legacy' | 'v2' | 'v3'
const oops = useFlag('typo') // TypeScript erroruseFlags([...flagNames])
Returns an object with values for multiple flags at once.
const { 'new-checkout': isNew, 'pricing-model': model } = useFlags([
'new-checkout',
'pricing-model',
])<Feature flag fallback? children>
Conditional rendering based on a flag value.
- Boolean
true→ renders children - Boolean
false→ rendersfallback(or null) - Non-boolean → renders children as render prop:
{(value) => <Component />}
<Feature flag="new-checkout" fallback={<OldCheckout />}>
<NewCheckout />
</Feature>
<Feature flag="max-upload-mb">
{(maxMB) => <Uploader limit={maxMB} />}
</Feature><Variant flag variants fallback?>
Renders a different subtree for each possible flag value. Ideal for A/B tests and multivariate experiments.
<Variant
flag="pricing-model"
variants={{
legacy: <LegacyPricing />,
v2: <PricingV2 />,
v3: <PricingV3 />,
}}
fallback={<DefaultPricing />}
/>jsonAdapter({ overrides })
Static adapter — forces specific flag values. Useful for local development, tests, and CI.
import { jsonAdapter } from '@flagskit/react'
<FlagProvider
adapter={jsonAdapter({ overrides: { 'new-checkout': true } })}
>httpAdapter({ url, refreshInterval?, headers? })
Fetches flag overrides from a JSON endpoint. Optionally polls so flags stay fresh without a page reload.
import { httpAdapter } from '@flagskit/react'
// Fetch once on mount
<FlagProvider adapter={httpAdapter({ url: '/api/flags' })}>
// With polling every 60 seconds
<FlagProvider adapter={httpAdapter({ url: '/api/flags', refreshInterval: 60_000 })}>
// With auth header
<FlagProvider adapter={httpAdapter({
url: '/api/flags',
headers: { Authorization: `Bearer ${token}` },
})}>The endpoint must return a flat JSON object: { "flag-name": value, ... }.
Rules
rules: [
// Equality match — all conditions AND
{ match: { role: 'admin' }, value: true },
// Percentage rollout — deterministic per userId
{ percentage: 20, value: true },
// Combined — both conditions must pass
{ match: { plan: 'pro' }, percentage: 30, value: true },
]Rules evaluate top-to-bottom. First matching rule wins.
License
MIT
