A “Flaggy” name for a not-at-all-flaggy library. A sturdy, type-safe React feature flag solution with hooks, SSR support, DevTools, percentage rollouts, user targeting, and A/B testing.
- Modern Hooks API -
useFeatureFlag,useFeatureFlags,useFeatureVariant - Full TypeScript Support - Complete type safety with declaration merging
- Multiple Flag Formats - Objects, arrays, nested structures
- Async Loading - Load flags from remote sources with auto-refresh
- User Targeting - Percentage rollouts and attribute-based targeting
- A/B Testing - Built-in variant selection for experiments
- DevTools - Debug panel with override capabilities
- SSR Support - Next.js, Remix, and other SSR frameworks
- Zero Dependencies - Lightweight and fast
npm install react-flaggy
# or
yarn add react-flaggyimport { FlagsProvider, useFeatureFlag } from 'react-flaggy';
// Define your flags
const flags = {
newDashboard: true,
betaFeatures: false,
darkMode: true
};
function App() {
return (
<FlagsProvider features={flags}>
<MyApp />
</FlagsProvider>
);
}
function MyApp() {
const hasNewDashboard = useFeatureFlag('newDashboard');
return hasNewDashboard ? <NewDashboard /> : <LegacyDashboard />;
}// Check a single flag
const isEnabled = useFeatureFlag('featureName');
// Get all flags
const allFlags = useFeatureFlags();
// A/B testing with variants
const { enabled, variant, value } = useFeatureVariant('experiment');// Declarative feature flag
<Feature name="newFeature" fallback={<Old />}>
<NewFeature />
</Feature>
// Provider with async loading
<FlagsProvider
loadFeatures={async () => fetch('/api/flags').then(r => r.json())}
user={{ id: 'user-123' }}
>
<App />
</FlagsProvider>const AdminPanel = withFeatureFlag('adminAccess')(Dashboard);// Simple object (recommended)
const flags = { feature1: true, feature2: false };
// Nested structure
const flags = {
admin: { dashboard: true },
checkout: { expressCheckout: true }
};
// Access: useFeatureFlag('admin/dashboard')
// String array (all enabled)
const flags = ['feature1', 'feature2'];
// Advanced config with rollouts
const flags = {
premium: {
enabled: true,
rollout: { percentage: 25 },
value: { theme: 'dark' }
}
};const flags = {
premiumFeature: {
enabled: true,
rollout: {
percentage: 10, // 10% rollout
userIds: ['vip-1'], // Always include
emailDomains: ['@company.com'], // Internal users
attributes: { plan: 'premium' } // Targeting
}
}
};
<FlagsProvider
features={flags}
user={{
id: 'user-123',
email: 'user@company.com',
attributes: { plan: 'premium' }
}}
>
<App />
</FlagsProvider>const flags = {
experiment: {
enabled: true,
variants: [
{ name: 'control', weight: 50 },
{ name: 'variant-a', weight: 50, value: { color: 'blue' } }
]
}
};
function Button() {
const { variant, value } = useFeatureVariant('experiment');
return <button style={{ color: value?.color }}>Click</button>;
}// feature-flags.d.ts - Get autocomplete!
declare module 'react-flaggy' {
export interface FeatureFlagNames {
'newDashboard': true;
'betaFeatures': true;
}
}
// Now with type safety
const isEnabled = useFeatureFlag('newDashboard'); // Autocomplete
const invalid = useFeatureFlag('typo'); // Type errorimport { FeatureFlagsDevTools } from 'react-flaggy';
<FlagsProvider
features={flags}
devTools={{
enabled: true,
allowUrlOverrides: true, // ?ff_override=flag:true
allowLocalStorageOverrides: true
}}
>
<App />
{process.env.NODE_ENV === 'development' && <FeatureFlagsDevTools />}
</FlagsProvider>// app/providers.tsx
'use client';
export function Providers({ children, flags }) {
return <FlagsProvider features={flags}>{children}</FlagsProvider>;
}
// app/layout.tsx
export default function RootLayout({ children }) {
const flags = { newFeature: true };
return (
<html><body><Providers flags={flags}>{children}</Providers></body></html>
);
}import { FlagsProvider, hydrateFlags } from 'react-flaggy';
export default function App({ Component, pageProps, flags }) {
return (
<FlagsProvider features={flags} hydrateFrom="__FEATURE_FLAGS__">
<Component {...pageProps} />
</FlagsProvider>
);
}For complete API documentation, examples, and guides, see the docs folder or visit our GitHub repository.
Contributions welcome! Please open an issue or PR.