Flagdeck
Pricing
Get Started

Docs

Docs


MCP Server

  • Overview & Setup

SDKs

  • JavaScript
    • Getting Started
    • Value Types
    • Evaluation Context
    • Configuration
    • Real Time Updates
    • Error Handling
    • Middleware
    • Offline Mode
    • API Reference
  • React
  • Vue

React SDK

The Flagdeck React SDK provides an easy way to use feature flags in React applications. Built on top of the JavaScript SDK, it offers React-specific components and hooks for a more idiomatic integration with your React projects.

Installation

Install the React SDK using npm, yarn, or pnpm:

# Using npm
npm install @flagdeck/react

# Using yarn
yarn add @flagdeck/react

# Using pnpm
pnpm add @flagdeck/react

The SDK automatically installs the Flagdeck JavaScript SDK as a dependency.

Quick Start

Wrap your application with the FlagdeckProvider and start using the hooks and components:

import React from 'react';
import { FlagdeckProvider, useFlag } from '@flagdeck/react';

function App() {
  return (
    <FlagdeckProvider 
      options={{ 
        apiKey: 'YOUR_API_KEY',
        realTimeUpdates: true,
      }}
    >
      <YourApp />
    </FlagdeckProvider>
  );
}

function YourApp() {
  const { isEnabled, isLoading } = useFlag('new-feature');
  
  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div>
      {isEnabled ? 'New feature is enabled!' : 'New feature is disabled'}
    </div>
  );
}

Provider Setup

FlagdeckProvider

The FlagdeckProvider initializes the Flagdeck SDK and provides the client instance to all child components.

<FlagdeckProvider
  options={{
    apiKey: 'your-api-key',             // Required
    debug: true,                        // Optional: enables debug logging
    realTimeUpdates: true,              // Optional: enables real-time flag updates via SSE
    timeout: 5000,                      // Optional: API request timeout in ms
    enableCache: true,                  // Optional: enables caching of flag evaluations
    cacheTimeout: 30000,                // Optional: cache TTL in ms
    enableOfflineMode: false,           // Optional: enables offline fallback
    enableAnalytics: true,              // Optional: enables analytics tracking
  }}
  initialContext={{                 // Optional: initial user context
    userId: 'user-123',                 // User identifier
    attributes: {                       // User attributes for targeting
      country: 'US',
      plan: 'premium'
    }
  }}
  readyDelayMs={100}                    // Optional: delay before marking SDK as ready
>
  {children}
</FlagdeckProvider>

Hooks

useFlagdeck

Access the Flagdeck client and context:

import { useFlagdeck } from '@flagdeck/react';

function UserSettings() {
  const { 
    client,               // Flagdeck client instance
    isReady,              // True when SDK is initialized and ready
    error,                // Error object if SDK initialization failed
    context,          // Current user context
    setContext        // Function to update user context
  } = useFlagdeck();
  
  // Update user context based on selections
  const handlePlanChange = (plan) => {
    setContext({
      ...context,
      attributes: {
        ...context?.attributes,
        plan
      }
    });
  };
  
  return (
    <div>
      {!isReady && <div>Loading SDK...</div>}
      {error && <div>Error: {error.message}</div>}
      {isReady && (
        <select 
          value={context?.attributes?.plan || 'free'} 
          onChange={(e) => handlePlanChange(e.target.value)}
        >
          <option value="free">Free Plan</option>
          <option value="premium">Premium Plan</option>
        </select>
      )}
    </div>
  );
}

useFlag

Evaluate a boolean feature flag:

import { useFlag } from '@flagdeck/react';

function FeatureComponent() {
  const { 
    isEnabled,            // Boolean flag value
    isLoading,            // True when flag is being evaluated
    error                 // Error object if evaluation failed
  } = useFlag('new-dashboard', false);  // 'false' is the default value
  
  if (isLoading) return <div>Loading...</div>;
  
  if (error) {
    console.warn(`Flag error: ${error.message}`);
    // Continue with default value
  }
  
  return (
    <div>
      {isEnabled ? <NewDashboard /> : <LegacyDashboard />}
    </div>
  );
}

useFlagValue

Get a feature flag value of any type:

import { useFlagValue } from '@flagdeck/react';

function ThemeComponent() {
  const { 
    value: theme,         // Flag value (can be any type)
    isLoading,            // True when flag is being evaluated
    error                 // Error object if evaluation failed
  } = useFlagValue('theme-variant', 'default');  // 'default' is the default value
  
  // Apply different styles based on theme value
  const themeStyles = {
    default: { background: 'white', color: 'black' },
    dark: { background: 'black', color: 'white' },
    blue: { background: '#0070F3', color: 'white' }
  };
  
  const style = themeStyles[theme] || themeStyles.default;
  
  return (
    <div style={style}>
      Current theme: {isLoading ? 'Loading...' : theme}
    </div>
  );
}

useFlags

Evaluate multiple flags at once:

import { useFlags } from '@flagdeck/react';

function FeatureDashboard() {
  const { 
    values,               // Record of flag keys to their values
    isLoading,            // True when flags are being evaluated
    errors                // Record of flag keys to error objects
  } = useFlags(['new-header', 'sidebar', 'analytics'], false);
  
  if (isLoading) return <div>Loading features...</div>;
  
  // Count enabled features
  const enabledCount = Object.values(values).filter(Boolean).length;
  
  return (
    <div>
      <p>Enabled features: {enabledCount}/{Object.keys(values).length}</p>
      
      {values['new-header'] && <NewHeader />}
      {values['sidebar'] && <Sidebar />}
      {values['analytics'] && <AnalyticsPanel />}
      
      {/* Example of handling errors */}
      {Object.entries(errors).map(([key, error]) => (
        <div key={key} className="error">
          Error with {key}: {error.message}
        </div>
      ))}
    </div>
  );
}

Components

FeatureFlag

Conditionally render content based on flag state:

import { FeatureFlag } from '@flagdeck/react';

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      
      <FeatureFlag
        flagKey="new-dashboard"
        defaultEnabled={false}
        fallback={<LegacyDashboard />}
        loadingContent={<div>Loading dashboard...</div>}
        onError={(error) => console.error('Dashboard flag error:', error)}
      >
        <NewDashboard />
      </FeatureFlag>
    </div>
  );
}

FlagValue

Render prop component for accessing flag values:

import { FlagValue } from '@flagdeck/react';

function ThemeWrapper({ children }) {
  return (
    <FlagValue flagKey="color-theme" defaultValue="default">
      {({ value: theme, isLoading }) => {
        // Apply theme classes or styles based on the theme value
        const themeClass = `theme-${isLoading ? 'default' : theme}`;
        
        return (
          <div className={themeClass}>
            {isLoading && <div className="theme-loading-indicator" />}
            {children}
          </div>
        );
      }}
    </FlagValue>
  );
}

WithFeature

Render prop alternative for conditional rendering:

import { WithFeature } from '@flagdeck/react';

function PaymentOptions() {
  return (
    <div>
      <h2>Payment Options</h2>
      
      {/* Always shown payment methods */}
      <CreditCardOption />
      <PayPalOption />
      
      {/* Conditionally shown new payment methods */}
      <WithFeature flagKey="crypto-payments">
        {({ isEnabled, isLoading }) => (
          <>
            {isLoading && <div>Loading payment options...</div>}
            {isEnabled && <CryptoPaymentOption />}
          </>
        )}
      </WithFeature>
    </div>
  );
}

Real-time Flag Updates

The React SDK supports real-time updates via Server-Sent Events (SSE). When enabled, flag changes made in the Flagdeck dashboard will be pushed to your application immediately without requiring page refreshes.

<FlagdeckProvider
  options={{
    apiKey: 'your-api-key',
    realTimeUpdates: true  // Enable real-time updates
  }}
>
  <App />
</FlagdeckProvider>

Then in your components, the hooks will automatically update with new values when flags change:

function FeatureMonitor() {
  const { isEnabled } = useFlag('monitored-feature');
  
  // This will automatically re-render when the flag changes
  return (
    <div>
      Feature status: {isEnabled ? 'Enabled' : 'Disabled'}
    </div>
  );
}

User Targeting

Set or update user context to target features to specific users:

function UserProfile() {
  const { context, setContext } = useFlagdeck();
  
  const handlePlanChange = (plan) => {
    setContext({
      ...context,
      attributes: {
        ...context?.attributes,
        plan
      }
    });
  };
  
  return (
    <div>
      <h2>User Profile</h2>
      <select 
        value={context?.attributes?.plan || 'free'}
        onChange={(e) => handlePlanChange(e.target.value)}
      >
        <option value="free">Free Plan</option>
        <option value="pro">Pro Plan</option>
        <option value="enterprise">Enterprise Plan</option>
      </select>
      
      {/* This component will re-evaluate flags when user context changes */}
      <FeaturePanel />
    </div>
  );
}

Error Handling

All hooks provide error information that you can use to handle different error scenarios:

function FeatureSection() {
  const { isEnabled, error } = useFlag('beta-feature');

  // Handle different error types
  if (error) {
    switch (error.type) {
      case 'notfound':
        return <div>This feature is not configured yet.</div>;
      
      case 'inactive':
        return <div>This feature is currently disabled.</div>;
      
      case 'configuration':
        return <div>There's a configuration issue with this feature.</div>;
      
      default:
        return <div>Error loading feature: {error.message}</div>;
    }
  }

  // Normal rendering when no errors
  return (
    <div>
      {isEnabled ? (
        <div>Beta feature is enabled!</div>
      ) : (
        <div>Beta feature is disabled.</div>
      )}
    </div>
  );
}

Advanced Patterns

Feature-gated Routing

import { useFlag } from '@flagdeck/react';
import { Navigate, Route } from 'react-router-dom';

function ProtectedFeatureRoute({ 
  flagKey, 
  fallbackPath = '/access-denied', 
  children 
}) {
  const { isEnabled, isLoading } = useFlag(flagKey);
  
  if (isLoading) {
    return <div>Loading access...</div>;
  }
  
  return isEnabled ? children : <Navigate to={fallbackPath} />;
}

// Usage in routes
function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route 
        path="/beta-feature" 
        element={
          <ProtectedFeatureRoute flagKey="beta-access">
            <BetaFeature />
          </ProtectedFeatureRoute>
        } 
      />
    </Routes>
  );
}

Lazy Loading with Feature Flags

import React, { lazy, Suspense } from 'react';
import { useFlag } from '@flagdeck/react';

// Define components
const LegacyComponent = lazy(() => import('./LegacyComponent'));
const NewComponent = lazy(() => import('./NewComponent'));

function FeatureWithLazyLoad() {
  const { isEnabled, isLoading } = useFlag('new-component');
  
  if (isLoading) {
    return <div>Loading feature flags...</div>;
  }
  
  // Choose which component to render based on flag
  const Component = isEnabled ? NewComponent : LegacyComponent;
  
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <Component />
    </Suspense>
  );
}

Theme System with Feature Flags

import { createContext, useContext } from 'react';
import { useFlagValue } from '@flagdeck/react';

// Create theme context
const ThemeContext = createContext({
  theme: 'default',
  isDark: false
});

function ThemeProvider({ children }) {
  const { value: theme } = useFlagValue('app-boot-theme', 'default');
  
  // Calculate theme properties
  const themeData = {
    theme,
    isDark: theme === 'dark' || theme === 'midnight',
    // Other theme properties...
  };
  
  useEffect(() => {
    // Apply theme to document root
    document.documentElement.setAttribute('data-theme', theme);
    
    if (themeData.isDark) {
      document.documentElement.classList.add('dark-mode');
    } else {
      document.documentElement.classList.remove('dark-mode');
    }
  }, [theme, themeData.isDark]);
  
  return (
    <ThemeContext.Provider value={themeData}>
      {children}
    </ThemeContext.Provider>
  );
}

// Hook to use theme
const useTheme = () => useContext(ThemeContext);

TypeScript Support

The React SDK is built with TypeScript and provides full type definitions:

import { useFlag, useFlagValue } from '@flagdeck/react';

function TypedComponent() {
  // Boolean flag
  const { isEnabled } = useFlag('feature-flag');
  
  // String flag with specific type
  const { value: theme } = useFlagValue<'light' | 'dark' | 'system'>('theme', 'system');
  
  // Number flag
  const { value: limit } = useFlagValue<number>('rate-limit', 100);
  
  // Complex object flag
  interface ConfigType {
    timeout: number;
    retries: number;
    endpoints: string[];
  }
  
  const defaultConfig: ConfigType = {
    timeout: 3000,
    retries: 3,
    endpoints: ['api.example.com']
  };
  
  const { value: config } = useFlagValue<ConfigType>('api-config', defaultConfig);
  
  return (
    <div>
      <p>Feature enabled: {String(isEnabled)}</p>
      <p>Theme: {theme}</p>
      <p>Rate limit: {limit}</p>
      <p>API timeout: {config.timeout}ms</p>
    </div>
  );
}

Best Practices

  1. Initialize Early: Place FlagdeckProvider high in your component tree to make flags available throughout your app.

  2. Handle Loading States: Always check isLoading before using flag values to avoid flash of incorrect content.

  3. Provide Default Values: Always specify default values that make sense for your application as fallbacks.

  4. Use Error Boundaries: Wrap flag-dependent components in error boundaries to catch evaluation errors.

  5. Batch Flag Evaluations: Use useFlags to evaluate multiple flags at once for better performance.

  6. Consider Server-Side Rendering: For SSR apps, provide sensible defaults and revalidate flags on the client side.

  7. Keep User Context Updated: Always update user context when user attributes change to ensure correct targeting.

  8. Cache Key Flags: Consider saving critical flags to local storage for offline use.

Performance Considerations

  • Minimize Re-renders: Use the useFlagdeck hook only in components that need to access or update the user context.

  • Optimize Flag Access: Use useFlags for multiple flags instead of multiple useFlag calls.

  • Enable Caching: Keep the default caching enabled to reduce API calls.

  • Use Real-time Updates Wisely: Only enable real-time updates for flags that need immediate propagation.

Next Steps

For more detailed information about the underlying JavaScript SDK features and configuration options, refer to the JavaScript SDK documentation.