@flagpool/react
v0.3.5
Published
Official Flagpool React SDK - React hooks and components for feature flags
Maintainers
Readme
@flagpool/react
Official React SDK for Flagpool - React hooks and components for feature flags.
Installation
npm install @flagpool/reactQuick Start
1. Wrap your app with the Provider
import { FlagpoolProvider } from '@flagpool/react';
function App() {
return (
<FlagpoolProvider
projectId="your-project-uuid"
apiKey="your-api-key"
decryptionKey="your-decryption-key"
context={{ userId: 'user-123', plan: 'pro' }}
>
<MyApp />
</FlagpoolProvider>
);
}2. Use hooks in your components
import { useFlag, useFlagValue } from '@flagpool/react';
function MyComponent() {
// Boolean flag
const showNewFeature = useFlag('new-feature');
// String/number/object flag
const buttonColor = useFlagValue('cta-button-color', 'blue');
return (
<div>
{showNewFeature && <NewFeature />}
<Button color={buttonColor}>Click me</Button>
</div>
);
}API Reference
Provider
<FlagpoolProvider>
Provides feature flag context to your React app.
<FlagpoolProvider
projectId="uuid-xxx" // Required: Project ID from dashboard
apiKey="pk_live_xxx" // Required: Environment-specific API key
decryptionKey="fp_dec_xxx" // Required: For CDN URL hash & target lists
context={{ userId: 'u1' }} // Optional: User context for targeting
cryptoAdapter={adapter} // Optional: For decrypting target lists
analytics={{ enabled: true }} // Optional: Enable analytics (paid plans)
pollingInterval={30000} // Optional: Polling interval in ms
streaming={true} // Optional: Enable real-time updates
urlOverride="https://..." // Optional: Complete URL override
onReady={() => {}} // Optional: Called when flags are ready
onError={(err) => {}} // Optional: Called on error
>
<App />
</FlagpoolProvider>Hooks
useFlag(flagKey, defaultValue?)
Get a boolean feature flag value.
const isEnabled = useFlag('new-feature');
const isEnabled = useFlag('new-feature', false); // with defaultuseFlagValue<T>(flagKey, defaultValue?)
Get a flag value of any type.
const color = useFlagValue('button-color', 'blue');
const limit = useFlagValue<number>('rate-limit', 100);
const config = useFlagValue<Config>('feature-config', defaultConfig);useFlagDetails<T>(flagKey, defaultValue?)
Get detailed flag information including loading state.
const { value, isReady, isLoading, error } = useFlagDetails('feature', false);
if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;
return value ? <NewFeature /> : <OldFeature />;useFlagpool()
Access the Flagpool client and context directly.
const { client, isReady, isLoading, error, setContext } = useFlagpool();
// Update context after login
useEffect(() => {
setContext({ userId: user.id, plan: user.plan });
}, [user]);Components
<Feature>
Conditionally render content based on a boolean flag.
// Simple usage
<Feature flag="new-checkout">
<NewCheckout />
</Feature>
// With fallback
<Feature flag="new-checkout" fallback={<OldCheckout />}>
<NewCheckout />
</Feature><Variant>
Render content when a flag matches a specific value. Perfect for A/B tests.
<Variant flag="cta-color" value="blue">
<BlueButton />
</Variant>
<Variant flag="cta-color" value="green">
<GreenButton />
</Variant>
<Variant flag="cta-color" value="red">
<RedButton />
</Variant><FeatureWithLoading>
Feature component with loading and error states.
<FeatureWithLoading
flag="new-checkout"
loading={<Spinner />}
error={(err) => <ErrorMessage message={err.message} />}
fallback={<OldCheckout />}
>
<NewCheckout />
</FeatureWithLoading>TypeScript Support
Full TypeScript support is included. Use generics for type-safe flag values:
interface FeatureConfig {
maxItems: number;
theme: 'light' | 'dark';
}
const config = useFlagValue<FeatureConfig>('feature-config', {
maxItems: 10,
theme: 'light',
});Server-Side Rendering (SSR)
For SSR frameworks like Next.js, the provider handles hydration automatically. Flags will show their default values during server render and update on the client once initialized.
For optimal SSR support, consider using useFlagDetails to handle loading states:
function MyComponent() {
const { value, isLoading } = useFlagDetails('feature', false);
// During SSR and initial client render
if (isLoading) {
return <DefaultView />;
}
return value ? <NewFeature /> : <OldFeature />;
}Analytics (Paid Plans Only)
Track flag evaluation counts to understand usage patterns. Analytics is opt-in and disabled by default.
Enabling Analytics
<FlagpoolProvider
projectId="your-project-uuid"
apiKey="your-api-key"
decryptionKey="your-decryption-key"
analytics={{ enabled: true }}
>
<App />
</FlagpoolProvider>Configuration Options
<FlagpoolProvider
// ...required props
analytics={{
enabled: true,
flushInterval: 60000, // Flush interval in ms (min: 30000)
flushThreshold: 100, // Flush after N evaluations
sampleRate: 1.0, // Sample rate (0.0 - 1.0)
}}
>
<App />
</FlagpoolProvider>| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | false | Enable/disable analytics |
| flushInterval | number | 60000 | Flush interval in ms (minimum: 30000) |
| flushThreshold | number | 100 | Flush after N evaluations |
| sampleRate | number | 1.0 | Sample rate (0.0 - 1.0) |
How It Works
- Evaluation counts are batched in memory
- Batches are sent asynchronously (fire-and-forget)
- Analytics never blocks flag evaluation or re-renders
- Data is aggregated daily in your Flagpool dashboard
- Note: Data is silently discarded for free plan projects
Debugging Analytics
Access analytics state via the useFlagpool hook:
function DebugPanel() {
const { client } = useFlagpool();
const analyticsState = client?.getAnalyticsState();
const flagsWithState = client?.getAllFlagsWithState();
console.log('Buffer:', analyticsState?.buffer);
console.log('Flags:', flagsWithState);
return null;
}Or use the browser console:
// Global debug object (browser only)
console.log(__FLAGPOOL__.state.analytics);
console.log(__FLAGPOOL__.state.flags);
__FLAGPOOL__.flushAnalytics();Target List Encryption
By default, target lists (used for inTargetList and notInTargetList operators) are encrypted in SDK exports to protect sensitive user data like emails and user IDs.
Using Encrypted Target Lists
If your environment has target list encryption enabled (the default), you need to provide a crypto adapter. The easiest way is to pass it directly to the FlagpoolProvider:
import { FlagpoolProvider, CryptoAdapter } from '@flagpool/react';
// Web Crypto adapter for browsers
const webCryptoAdapter: CryptoAdapter = {
async decrypt(ciphertext, key, iv, tag) {
const keyBase64 = key.startsWith('tlk_') ? key.slice(4) : key;
const keyBytes = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0));
const ivBytes = Uint8Array.from(atob(iv), c => c.charCodeAt(0));
const ciphertextBytes = Uint8Array.from(atob(ciphertext), c => c.charCodeAt(0));
const tagBytes = Uint8Array.from(atob(tag), c => c.charCodeAt(0));
const cryptoKey = await crypto.subtle.importKey(
'raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']
);
const combined = new Uint8Array(ciphertextBytes.length + tagBytes.length);
combined.set(ciphertextBytes, 0);
combined.set(tagBytes, ciphertextBytes.length);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: ivBytes }, cryptoKey, combined
);
return new TextDecoder().decode(decrypted);
}
};
function App() {
return (
<FlagpoolProvider
projectId="your-project-uuid"
apiKey="your-api-key"
decryptionKey="tlk_xxx..."
cryptoAdapter={webCryptoAdapter}
context={{ userId: 'user-123' }}
>
<MyApp />
</FlagpoolProvider>
);
}Error: "Encrypted target lists received but no crypto adapter configured"
If you see this error, it means:
- Your environment has target list encryption enabled (the default)
- You haven't provided a crypto adapter
Solutions:
Provide a crypto adapter (recommended for production):
<FlagpoolProvider cryptoAdapter={webCryptoAdapter} // ... other props >Disable encryption (for development/testing):
- Go to Flagpool Dashboard → Settings → Environments
- Toggle off "Target List Encryption" for your environment
⚠️ Security Note: Disabling encryption exposes target list values (emails, user IDs, etc.) in plaintext. Only disable for development or non-sensitive data.
License
MIT © Flagpool
