Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add featbit/featbit-skills --skill "featbit-react-client-sdk"
Install specific skill from multi-skill repository
# Description
Expert guidance for integrating FeatBit React Client SDK in React and Next.js applications. Use when users work with React, need React hooks, or ask about React/Next.js feature flag integration.
# SKILL.md
name: featbit-react-client-sdk
description: Expert guidance for integrating FeatBit React Client SDK in React and Next.js applications. Use when users work with React, need React hooks, or ask about React/Next.js feature flag integration.
appliesTo:
- "/*.jsx"
- "/.tsx"
- "/App.jsx"
- "/App.tsx"
- "/_app.js"
- "*/_app.tsx"
FeatBit React Client SDK Expert
Expert knowledge for integrating FeatBit in React and Next.js applications with hooks and components.
Source: https://github.com/featbit/featbit-react-client-sdk
โจ Built on top of: @featbit/js-client-sdk
Compatibility
- React: 16.3.0+ (16.8.0+ for hooks)
- Next.js: Client-side only (not SSR compatible)
โ ๏ธ Next.js Note: This SDK is not compatible with server-side rendering. Use @featbit/node-server-sdk for SSR.
Installation
npm install @featbit/react-client-sdk
Prerequisites
Obtain these values first:
- Environment Secret: How to get
- SDK URLs: How to get
Quick Start
import { createRoot } from 'react-dom/client';
import { asyncWithFbProvider, useFlags } from '@featbit/react-client-sdk';
function Game() {
return <div>This is a game</div>;
}
function App() {
const flags = useFlags();
const gameEnabled = flags['game-enabled'];
return (
<div>
{gameEnabled ? <Game /> : <div>The game is not enabled</div>}
</div>
);
}
(async () => {
const config = {
options: {
user: {
name: 'the user name',
keyId: 'fb-demo-user-key',
customizedProperties: []
},
sdkKey: 'YOUR_ENVIRONMENT_SECRET',
streamingUrl: 'wss://app-eval.featbit.co',
eventsUrl: 'https://app-eval.featbit.co'
}
};
const root = createRoot(document.getElementById('root'));
const Provider = await asyncWithFbProvider(config);
root.render(
<Provider>
<App />
</Provider>
);
})();
Provider Setup
Method 1: asyncWithFbProvider (Async Initialization)
import { asyncWithFbProvider } from '@featbit/react-client-sdk';
const config = {
options: {
user: {
name: 'User Name',
keyId: 'user-unique-id',
customizedProperties: [
{ name: 'country', value: 'US' },
{ name: 'age', value: '25' }
]
},
sdkKey: 'your-sdk-key',
streamingUrl: 'wss://app-eval.featbit.co',
eventsUrl: 'https://app-eval.featbit.co'
}
};
const Provider = await asyncWithFbProvider(config);
root.render(
<Provider>
<App />
</Provider>
);
Method 2: FbProvider (Synchronous)
import { FbProvider } from '@featbit/react-client-sdk';
function Root() {
const config = {
user: {
name: 'User Name',
keyId: 'user-unique-id',
customizedProperties: []
},
sdkKey: 'your-sdk-key',
streamingUrl: 'wss://app-eval.featbit.co',
eventsUrl: 'https://app-eval.featbit.co'
};
return (
<FbProvider config={config}>
<App />
</FbProvider>
);
}
Using Hooks
useFlags - Get All Flags
import { useFlags } from '@featbit/react-client-sdk';
function MyComponent() {
const flags = useFlags();
const newFeatureEnabled = flags['new-feature'] || false;
const theme = flags['theme'] || 'default';
const maxItems = flags['max-items'] || 10;
return (
<div>
{newFeatureEnabled && <NewFeature />}
<div className={theme}>Content</div>
</div>
);
}
useFbClient - Access Client and Flags
import { useFbClient } from '@featbit/react-client-sdk';
function MyComponent() {
const { flags, client } = useFbClient();
const handleAction = () => {
// Track custom event
client.sendCustomEvent('button-clicked');
};
return (
<div>
<button onClick={handleAction}>
{flags['button-text'] || 'Click Me'}
</button>
</div>
);
}
Custom Hook for Specific Flag
import { useFlags } from '@featbit/react-client-sdk';
function useFeatureFlag(flagKey, defaultValue = false) {
const flags = useFlags();
return flags[flagKey] ?? defaultValue;
}
// Usage
function Component() {
const isEnabled = useFeatureFlag('new-feature', false);
const theme = useFeatureFlag('theme', 'light');
return (
<div className={theme}>
{isEnabled && <NewFeature />}
</div>
);
}
Identify User (Switch User)
import { useFbClient } from '@featbit/react-client-sdk';
function UserSwitcher() {
const { client } = useFbClient();
const switchToUser = async (userId, userName) => {
const newUser = {
keyId: userId,
name: userName,
customizedProperties: [
{ name: 'role', value: 'admin' }
]
};
await client.identify(newUser);
};
return (
<button onClick={() => switchToUser('user-2', 'Jane')}>
Switch User
</button>
);
}
Track Custom Events
import { useFbClient } from '@featbit/react-client-sdk';
function PurchaseButton() {
const { client } = useFbClient();
const handlePurchase = async () => {
// Process purchase...
// Track conversion event
client.sendCustomEvent('purchase-completed');
// Track with value
client.trackMetric('revenue', 99.99);
};
return <button onClick={handlePurchase}>Buy Now</button>;
}
Complete React App Example
import React from 'react';
import { createRoot } from 'react-dom/client';
import { asyncWithFbProvider, useFlags, useFbClient } from '@featbit/react-client-sdk';
// Feature Component
function NewDashboard() {
return (
<div className="new-dashboard">
<h2>New Dashboard Design</h2>
<p>This is the new and improved dashboard!</p>
</div>
);
}
function OldDashboard() {
return (
<div className="old-dashboard">
<h2>Dashboard</h2>
<p>This is the current dashboard</p>
</div>
);
}
// Main App
function App() {
const flags = useFlags();
const { client } = useFbClient();
const useNewDashboard = flags['new-dashboard'] || false;
const theme = flags['theme'] || 'light';
const handleFeedback = () => {
client.sendCustomEvent('feedback-clicked');
};
return (
<div className={`app theme-${theme}`}>
<header>
<h1>My Application</h1>
<button onClick={handleFeedback}>Give Feedback</button>
</header>
<main>
{useNewDashboard ? <NewDashboard /> : <OldDashboard />}
</main>
</div>
);
}
// Initialize and Render
(async () => {
const config = {
options: {
user: {
name: 'John Doe',
keyId: 'user-123',
customizedProperties: [
{ name: 'role', value: 'user' },
{ name: 'plan', value: 'premium' }
]
},
sdkKey: process.env.REACT_APP_FEATBIT_SDK_KEY,
streamingUrl: 'wss://app-eval.featbit.co',
eventsUrl: 'https://app-eval.featbit.co'
}
};
const root = createRoot(document.getElementById('root'));
const Provider = await asyncWithFbProvider(config);
root.render(
<React.StrictMode>
<Provider>
<App />
</Provider>
</React.StrictMode>
);
})();
Next.js Integration (Client-Side Only)
// pages/_app.js
import { useEffect, useState } from 'react';
import { FbProvider } from '@featbit/react-client-sdk';
function MyApp({ Component, pageProps }) {
const [fbConfig, setFbConfig] = useState(null);
useEffect(() => {
// Only initialize on client-side
if (typeof window !== 'undefined') {
setFbConfig({
user: {
keyId: 'user-id',
name: 'User Name',
customizedProperties: []
},
sdkKey: process.env.NEXT_PUBLIC_FEATBIT_SDK_KEY,
streamingUrl: 'wss://app-eval.featbit.co',
eventsUrl: 'https://app-eval.featbit.co'
});
}
}, []);
if (!fbConfig) {
return <Component {...pageProps} />;
}
return (
<FbProvider config={fbConfig}>
<Component {...pageProps} />
</FbProvider>
);
}
export default MyApp;
// pages/index.js
import { useFlags } from '@featbit/react-client-sdk';
export default function Home() {
const flags = useFlags();
const newFeature = flags['new-feature'] || false;
return (
<div>
<h1>Home Page</h1>
{newFeature && <div>New Feature!</div>}
</div>
);
}
Advanced Patterns
Feature Flag HOC
import { useFlags } from '@featbit/react-client-sdk';
function withFeatureFlag(Component, flagKey, defaultValue = false) {
return function FeatureFlagWrapper(props) {
const flags = useFlags();
const isEnabled = flags[flagKey] ?? defaultValue;
if (!isEnabled) {
return null;
}
return <Component {...props} />;
};
}
// Usage
const BetaFeature = withFeatureFlag(MyBetaComponent, 'beta-feature');
function App() {
return (
<div>
<BetaFeature /> {/* Only renders if flag is enabled */}
</div>
);
}
Conditional Rendering Component
import { useFlags } from '@featbit/react-client-sdk';
function FeatureFlag({ flag, fallback = null, children }) {
const flags = useFlags();
const isEnabled = flags[flag] || false;
return isEnabled ? children : fallback;
}
// Usage
function App() {
return (
<div>
<FeatureFlag flag="new-ui" fallback={<OldUI />}>
<NewUI />
</FeatureFlag>
</div>
);
}
A/B Test Component
import { useFlags, useFbClient } from '@featbit/react-client-sdk';
import { useEffect } from 'react';
function ABTest({ flagKey, variants, defaultVariant = 'A' }) {
const flags = useFlags();
const { client } = useFbClient();
const variant = flags[flagKey] || defaultVariant;
useEffect(() => {
// Track variant shown
client.sendCustomEvent(`${flagKey}-variant-${variant}`);
}, [variant]);
const Component = variants[variant] || variants[defaultVariant];
return <Component />;
}
// Usage
function App() {
return (
<ABTest
flagKey="landing-page"
variants={{
A: LandingPageA,
B: LandingPageB,
C: LandingPageC
}}
defaultVariant="A"
/>
);
}
TypeScript Support
import { useFlags, useFbClient, FbClient } from '@featbit/react-client-sdk';
interface Flags {
'new-feature': boolean;
'theme': string;
'max-items': number;
}
function MyComponent() {
const flags = useFlags() as Flags;
const { client } = useFbClient();
const isEnabled: boolean = flags['new-feature'];
const theme: string = flags['theme'] || 'light';
const maxItems: number = flags['max-items'] || 10;
return <div className={theme}>Content</div>;
}
Best Practices
1. Environment Variables
// .env.local
REACT_APP_FEATBIT_SDK_KEY=your-sdk-key
REACT_APP_FEATBIT_STREAMING_URL=wss://app-eval.featbit.co
REACT_APP_FEATBIT_EVENTS_URL=https://app-eval.featbit.co
// Usage
const config = {
sdkKey: process.env.REACT_APP_FEATBIT_SDK_KEY,
streamingUrl: process.env.REACT_APP_FEATBIT_STREAMING_URL,
eventsUrl: process.env.REACT_APP_FEATBIT_EVENTS_URL
};
2. Loading States
import { useState, useEffect } from 'react';
import { useFbClient } from '@featbit/react-client-sdk';
function App() {
const [isReady, setIsReady] = useState(false);
const { flags, client } = useFbClient();
useEffect(() => {
if (client) {
client.waitForInitialization().then(() => {
setIsReady(true);
});
}
}, [client]);
if (!isReady) {
return <div>Loading...</div>;
}
return <div>App Content</div>;
}
3. Error Boundaries
class FeatureFlagErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Feature flag error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong with feature flags</div>;
}
return this.props.children;
}
}
// Usage
<FeatureFlagErrorBoundary>
<App />
</FeatureFlagErrorBoundary>
Troubleshooting
Hooks Not Working
Ensure React version is 16.8.0+:
npm list react
Next.js SSR Issues
// Use dynamic import with ssr: false
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(
() => import('../components/FeatureComponent'),
{ ssr: false }
);
Flags Not Updating
// Listen for updates
const { client } = useFbClient();
useEffect(() => {
const handleUpdate = () => {
console.log('Flags updated');
};
client.on('ff_update', handleUpdate);
return () => {
client.off('ff_update', handleUpdate);
};
}, [client]);
Additional Resources
- GitHub: https://github.com/featbit/featbit-react-client-sdk
- NPM: https://www.npmjs.com/package/@featbit/react-client-sdk
- Examples: https://github.com/featbit/featbit-react-client-sdk/tree/main/examples
- Base SDK: https://github.com/featbit/featbit-js-client-sdk
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.