@tactic-social/tas-feature-flags-react
v0.1.0
Published
A lightweight, type-safe feature flag library for React applications
Maintainers
Readme
@tactic-social/tas-feature-flags-react
A lightweight, type-safe feature flag library for React applications with zero dependencies. Built and maintained by Tactic Social.
Why Use This Library?
- Type-Safe: Full TypeScript support with generics for any value type
- Flexible Values: Support for booleans, strings, numbers, arrays, and nested objects
- Nested Config: Organize flags hierarchically, automatically flattened to dotted notation
- Zero Dependencies: Only requires React as a peer dependency
- SSR/SSG Ready: Works seamlessly with Next.js, Remix, Astro, and other frameworks
- Declarative & Imperative: Use hooks or components based on your preference
- Tiny Bundle: ~2KB gzipped
- Tree-Shakeable: Only import what you need
Installation
npm install @tactic-social/tas-feature-flags-reactyarn add @tactic-social/tas-feature-flags-reactpnpm add @tactic-social/tas-feature-flags-reactQuick Start
1. Wrap Your App with the Provider
import { FeatureFlagProvider } from '@tactic-social/tas-feature-flags-react';
const config = {
features: {
auth: true,
analytics: false,
},
platforms: ['facebook', 'instagram'],
limits: {
maxAccounts: 5,
},
};
function App() {
return (
<FeatureFlagProvider config={config}>
<YourApp />
</FeatureFlagProvider>
);
}2. Use Flags in Your Components
Option A: Using Hooks (Imperative)
import { useFeatureFlag, useFeatureEnabled } from '@tactic-social/tas-feature-flags-react';
function Dashboard() {
// Get any value with type safety
const platforms = useFeatureFlag<string[]>('platforms');
const maxAccounts = useFeatureFlag<number>('limits.maxAccounts');
// Check if a feature is enabled
const hasAuth = useFeatureEnabled('features.auth');
const hasFacebook = useFeatureEnabled('platforms', 'facebook');
return (
<div>
{hasAuth && <LoginButton />}
{platforms?.map(platform => <PlatformCard key={platform} name={platform} />)}
<p>Max accounts: {maxAccounts}</p>
</div>
);
}Option B: Using Component (Declarative)
import { Flag } from '@tactic-social/tas-feature-flags-react';
function Dashboard() {
return (
<div>
{/* Simple boolean check */}
<Flag flag="features.auth">
<LoginButton />
</Flag>
{/* Check if value exists in array */}
<Flag flag="platforms" value="facebook">
<FacebookWidget />
</Flag>
{/* Nested flags */}
<Flag flag="features.analytics">
<div>
<h2>Analytics</h2>
<Flag flag="platforms" value="instagram">
<InstagramStats />
</Flag>
</div>
</Flag>
</div>
);
}API Reference
<FeatureFlagProvider>
The provider component that makes feature flags available to your app.
Props:
config:Record<string, any>(required) - Your feature flag configuration (supports nested objects)children:ReactNode(required) - Your application components
Example:
<FeatureFlagProvider config={{ features: { auth: true } }}>
<App />
</FeatureFlagProvider>useFeatureFlag<T>(key: string)
Get a feature flag value with type safety.
Parameters:
key:string- The feature flag key (supports dotted notation for nested values)
Returns: T | undefined - The flag value or undefined if not found
Examples:
const isEnabled = useFeatureFlag<boolean>('features.auth'); // boolean
const platforms = useFeatureFlag<string[]>('platforms'); // string[]
const maxCount = useFeatureFlag<number>('limits.maxAccounts'); // number
const theme = useFeatureFlag<string>('theme'); // stringuseFeatureEnabled(key: string, value?: any)
Check if a feature is enabled (boolean check).
Parameters:
key:string- The feature flag keyvalue?:any- Optional value to check in arrays
Returns: boolean - True if enabled, false otherwise
Behavior:
- For booleans: Returns the truthiness of the flag
- For arrays with
valueparam: Returns true if value exists in array - For other types with
valueparam: Returns true if flag equals value - Returns false if flag is undefined
Examples:
const isAuthEnabled = useFeatureEnabled('features.auth'); // true/false
const hasFacebook = useFeatureEnabled('platforms', 'facebook'); // true if 'facebook' in array
const isDarkMode = useFeatureEnabled('theme', 'dark'); // true if theme === 'dark'<Flag>
Declarative component for conditional rendering based on feature flags.
Props:
flag:string(required) - The feature flag keyvalue?:any(optional) - Value to check in arrays or match exactlychildren:ReactNode(required) - Content to render when enabled
Examples:
{/* Render if flag is truthy */}
<Flag flag="features.auth">
<LoginButton />
</Flag>
{/* Render if value exists in array */}
<Flag flag="platforms" value="facebook">
<FacebookWidget />
</Flag>
{/* Render if exact match */}
<Flag flag="theme" value="dark">
<DarkModeStyles />
</Flag>useFeatureFlags()
Access the full feature flags context (advanced usage).
Returns:
{
flags: Record<string, any>; // All flattened flags
getFeatureValue: <T>(key: string) => T | undefined; // Get value (same as useFeatureFlag)
isFeatureEnabled: (key: string, value?: any) => boolean; // Check enabled (same as useFeatureEnabled)
}Example:
const { flags, getFeatureValue, isFeatureEnabled } = useFeatureFlags();
// Access all flags
console.log(flags); // { 'features.auth': true, 'platforms': [...], ... }flattenConfig(obj: Record<string, any>)
Utility function to flatten nested objects into dotted notation (rarely needed directly).
Example:
import { flattenConfig } from '@tactic-social/tas-feature-flags-react';
const nested = {
features: { auth: true },
platforms: ['facebook']
};
const flat = flattenConfig(nested);
// { 'features.auth': true, 'platforms': ['facebook'] }Advanced Usage
Nested Configuration
Organize your flags hierarchically for better structure:
const config = {
features: {
auth: {
oauth: true,
providers: ['google', 'github'],
},
analytics: false,
},
limits: {
maxAccounts: 5,
maxPosts: 100,
},
};
// Access nested values with dotted notation
const oauth = useFeatureFlag<boolean>('features.auth.oauth');
const providers = useFeatureFlag<string[]>('features.auth.providers');Loading Config from API
import { useState, useEffect } from 'react';
import { FeatureFlagProvider } from '@tactic-social/tas-feature-flags-react';
function App() {
const [config, setConfig] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/feature-flags')
.then(res => res.json())
.then(data => {
setConfig(data);
setLoading(false);
});
}, []);
if (loading) return <LoadingSpinner />;
return (
<FeatureFlagProvider config={config}>
<Dashboard />
</FeatureFlagProvider>
);
}Next.js App Router (SSR)
// app/layout.tsx
import { FeatureFlagProvider } from '@tactic-social/tas-feature-flags-react';
import config from './config/features.json';
export default function RootLayout({ children }) {
return (
<html>
<body>
<FeatureFlagProvider config={config}>
{children}
</FeatureFlagProvider>
</body>
</html>
);
}
// app/page.tsx (Server Component)
import { flattenConfig } from '@tactic-social/tas-feature-flags-react';
import config from './config/features.json';
export default function Page() {
const flags = flattenConfig(config);
const isEnabled = flags['features.auth'];
return <div>{isEnabled ? 'Enabled' : 'Disabled'}</div>;
}
// components/ClientComponent.tsx (Client Component)
'use client';
import { useFeatureEnabled } from '@tactic-social/tas-feature-flags-react';
export default function ClientComponent() {
const isEnabled = useFeatureEnabled('features.auth');
return <div>{isEnabled ? 'Enabled' : 'Disabled'}</div>;
}TypeScript Type Safety
// Define your config type
interface MyConfig {
features: {
auth: boolean;
analytics: boolean;
};
platforms: string[];
limits: {
maxAccounts: number;
};
}
// Use with type safety
const platforms = useFeatureFlag<string[]>('platforms');
const maxAccounts = useFeatureFlag<number>('limits.maxAccounts');Framework Compatibility
This library works with any React-based framework:
- ✅ Next.js (App Router & Pages Router)
- ✅ Remix
- ✅ Astro
- ✅ Vite
- ✅ Create React App
- ✅ Gatsby
- ✅ TanStack Start
Best Practices
1. Choose the Right API
Use hooks when:
- You need the flag value for logic
- Multiple conditions based on flags
- Dynamic behavior based on flag values
Use <Flag> component when:
- Simple show/hide logic
- Declarative, readable JSX
- No need for flag value in logic
2. Organize Flags Hierarchically
// Good - Organized structure
const config = {
features: {
auth: true,
analytics: false,
},
limits: {
maxAccounts: 5,
},
};
// Avoid - Flat with unclear naming
const config = {
feature_auth: true,
feature_analytics: false,
limit_max_accounts: 5,
};3. Handle Undefined Flags
// Good - Check for undefined
const platforms = useFeatureFlag<string[]>('platforms');
if (!platforms) return <EmptyState />;
// Bad - Will crash if undefined
const platforms = useFeatureFlag<string[]>('platforms');
return <div>{platforms.join(', ')}</div>;4. Use Consistent Key Naming
// Good - Dotted notation
'features.auth'
'features.analytics'
'limits.maxAccounts'
// Avoid - Inconsistent
'feature_auth'
'analytics-enabled'Migration from v0.x
If you're upgrading from version 0.x, here are the breaking changes:
Provider Prop Change
// Before (v0.x)
<FeatureFlagProvider initialFlags={{ feature1: true }}>
// After (v1.x)
<FeatureFlagProvider config={{ feature1: true }}>Hook Return Type Change
// Before (v0.x)
const isEnabled = useFeatureFlag('feature1'); // Returns: boolean
// After (v1.x)
const value = useFeatureFlag('feature1'); // Returns: any
const isEnabled = useFeatureEnabled('feature1'); // Returns: booleanRemoved Methods
// Before (v0.x) - Had setFlag and setFlags
const { setFlag, setFlags } = useFeatureFlags();
// After (v1.x) - Read-only flags
const { flags, getFeatureValue, isFeatureEnabled } = useFeatureFlags();Development
# Install dependencies
pnpm install
# Build the package
pnpm run build
# Run tests
pnpm test
# Type check
pnpm run type-check
# Lint
pnpm run lintContributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT © Tactic Social
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Documentation: GitHub Repository
Made with ❤️ by Tactic Social
