Configuration Guide

Set up the FlagsProvider and configure your feature flags for maximum flexibility

Basic Setup

Get your application ready for feature flags

The first step is to wrap your application with the FlagsProvider component. This provides the feature flags context to all child components.

app.tsx (Basic Setup)
import React from 'react';
import { FlagsProvider } from '@pursuit-amsterdam/feature-flags';
import { AppComponent } from './components/AppComponent';

// Define your feature flags
const featureFlags = {
  newHeader: true,
  betaFeatures: false,
  darkMode: true,
} as const;

function App() {
  return (
    <FlagsProvider config={featureFlags}>
      <AppComponent />
    </FlagsProvider>
  );
}

export default App;

That's it!

With just this setup, all child components can now access and use feature flags. The FlagsProvider handles state management, persistence, and synchronization automatically.

FlagsProvider Configuration

Understanding all available options

The FlagsProvider accepts several configuration options to customize its behavior:

Configuration Options

config (required)

The initial feature flags configuration object

children (required)

React children components to wrap

Automatic Features

The FlagsProvider automatically handles localStorage persistence, cross-tab synchronization, and encryption without additional configuration.
Advanced FlagsProvider Setup
import { FlagsProvider } from '@pursuit-amsterdam/feature-flags';

const flags = {
  // Your flags here
} as const;

function App() {
  return (
    <FlagsProvider config={flags}>
      }}
      syncAcrossTabs={true}
    >
      <YourApp />
    </FlagsProvider>
  );
}

Defining Feature Flags

Best practices for structuring your flags

Feature flags should be defined as a configuration object with meaningful names and appropriate default values.

flags.ts (Recommended Structure)
// flags.ts
export const featureFlags = {
  // UI/UX Features
  useNewHeader: true,
  enableDarkMode: false,
  showNotifications: true,
  
  // Experimental Features
  betaSearch: false,
  advancedFilters: false,
  
  // Access Control
  adminPanel: false,
  premiumFeatures: false,
  
  // Development/Debug
  debugMode: false,
  verboseLogging: false,
  
  // Business Logic
  newPaymentFlow: false,
  enhancedAnalytics: true,
} as const;

// Export the type for TypeScript support
export type AppFlags = typeof featureFlags;

Naming Conventions

useNewHeader - Clear action
enableDarkMode - Clear intention
showBetaFeatures - Clear visibility
flag1 - Not descriptive
temp - Not permanent naming

Flag Categories

UI/UX Flags

  • • Visual design changes
  • • Layout modifications
  • • Theme switches

Feature Flags

  • • New functionality
  • • Beta features
  • • Experimental features

Access Control

  • • Role-based features
  • • Premium content
  • • Admin functionality

Operational

  • • Debug modes
  • • Maintenance modes
  • • Performance toggles

Environment Variables

Integrating with your deployment pipeline

The library provides a boolFromEnv utility function to easily integrate feature flags with environment variables.

Environment Integration
import { boolFromEnv } from '@pursuit-amsterdam/feature-flags/server';

export const featureFlags = {
  // Read from environment variables with fallback defaults
  useNewHeader: boolFromEnv(process.env.REACT_APP_NEW_HEADER, true),
  betaFeatures: boolFromEnv(process.env.REACT_APP_BETA_FEATURES, false),
  debugMode: boolFromEnv(process.env.REACT_APP_DEBUG_MODE, false),
  
  // Can also use direct boolean values
  alwaysEnabled: true,
  neverEnabled: false,
} as const;

Environment Files

Create different environment files for each deployment stage:

.env.development

REACT_APP_NEW_HEADER=true
REACT_APP_BETA_FEATURES=true
REACT_APP_DEBUG_MODE=true

.env.staging

REACT_APP_NEW_HEADER=true
REACT_APP_BETA_FEATURES=false
REACT_APP_DEBUG_MODE=false

.env.production

REACT_APP_NEW_HEADER=false
REACT_APP_BETA_FEATURES=false
REACT_APP_DEBUG_MODE=false

This Demo Configuration

This demo application uses environment variables with sensible defaults. You can see the actual configuration in packages/demo-app/src/config/flags.ts.

Current Environment Values

See how your current flags are configured

useNewHeadertrue
showBetaFeaturesfalse
enableDarkModefalse
useAdvancedSearchtrue
showNotificationstrue
enableAnalyticsfalse

Demo App Pattern

Real-world configuration pattern used in this demo

This demo application demonstrates a production-ready pattern for organizing feature flags using environment variables with fallback defaults. Let's examine the actual configuration used in packages/demo-app/src/config/flags.ts:

packages/demo-app/src/config/flags.ts
import { boolFromEnv } from '@pursuit-amsterdam/feature-flags/server';

// Demo feature flags configuration
export const demoFlags = {
    useNewHeader: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_USE_NEW_HEADER,
        true
    ),
    showBetaFeatures: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_SHOW_BETA_FEATURES,
        false
    ),
    enableDarkMode: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_ENABLE_DARK_MODE,
        false
    ),
    useAdvancedSearch: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_USE_ADVANCED_SEARCH,
        true
    ),
    showNotifications: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_SHOW_NOTIFICATIONS,
        true
    ),
    enableAnalytics: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_ENABLE_ANALYTICS,
        false
    ),
    premiumFeatures: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_PREMIUM_FEATURES,
        false
    ),
    maintenanceMode: boolFromEnv(
        process.env.NEXT_PUBLIC_FLAG_MAINTENANCE_MODE,
        false
    ),
    debugMode: boolFromEnv(process.env.NEXT_PUBLIC_FLAG_DEBUG_MODE, false),
    superAdmin: boolFromEnv(process.env.NEXT_PUBLIC_FLAG_SUPER_ADMIN, false),
} as const;

export type DemoFlags = typeof demoFlags;

Pattern Analysis

1. Consistent Naming Convention

All environment variables follow the NEXT_PUBLIC_FLAG_* pattern:

  • NEXT_PUBLIC_ - Next.js client-side access
  • FLAG_ - Clear identification as feature flag
  • DESCRIPTIVE_NAME - Self-documenting purpose

2. Strategic Default Values

Each flag has a carefully chosen default that makes sense for development:

Enabled by Default (true)
useNewHeader - Safe UI updates
useAdvancedSearch - Enhanced UX
showNotifications - User engagement
Disabled by Default (false)
showBetaFeatures - Experimental
enableAnalytics - Privacy-first
premiumFeatures - Access control
maintenanceMode - Emergency only

3. TypeScript Integration

The pattern includes complete TypeScript support:

  • as const ensures immutability
  • typeof demoFlags derives accurate types
  • • Exported type enables IDE autocomplete and validation

Benefits of This Pattern

🔧

Environment Flexibility

Override any flag via environment variables without code changes

Development Ready

Sensible defaults mean immediate productivity

🛡️

Type Safety

Full TypeScript support with autocomplete

📝

Self-Documenting

Clear naming makes purpose obvious

🔄

Deployment Control

Different flags per environment (dev/staging/prod)

🎯

Categorized

Logical grouping by functionality

Adopting This Pattern

Copy this pattern to your own projects by: 1) Creating a similar flags.ts file, 2) Using consistent naming for environment variables, 3) Choosing appropriate defaults for your use cases, and 4) Exporting TypeScript types for better developer experience.

Storage & Encryption

How flags are persisted and secured

The library automatically handles flag persistence using encrypted localStorage. This ensures that user preferences are saved and synchronized across browser sessions.

Storage Features

Automatic Encryption

All flag data is encrypted before being stored in localStorage

Cross-tab Sync

Changes in one tab are instantly reflected in all other tabs

Fallback to Defaults

If localStorage is unavailable, flags fall back to their default values

Custom Encryption Configuration
import { FlagsProvider } from '@pursuit-amsterdam/feature-flags';

const customEncryption = {
  secretKey: process.env.REACT_APP_ENCRYPTION_KEY,
  algorithm: 'AES-GCM',
  keyDerivation: 'PBKDF2'
};

function App() {
  return (
    <FlagsProvider 
      config={flags}
      encryption={customEncryption}
      config={featureFlags}
    >
      <YourApp />
    </FlagsProvider>
  );
}

TypeScript Setup

Getting the most out of type safety

The library is built with TypeScript and provides full type safety for your feature flags. Here's how to set it up properly:

Complete TypeScript Setup
// types/flags.ts
export const appFlags = {
  useNewHeader: true,
  showBetaFeatures: false,
  enableDarkMode: true,
  premiumFeatures: false,
} as const;

// Export the type for use throughout your app
export type AppFlags = typeof appFlags;

// Optional: Create a branded type for even stronger typing
export type FlagKey = keyof AppFlags;
Using TypeScript with hooks
import { useFeatureFlags } from '@pursuit-amsterdam/feature-flags';
import type { AppFlags, FlagKey } from './types/flags';

function MyComponent() {
  // Full type safety with auto-completion
  const { flags, toggleFlag, setFlag } = useFeatureFlags<AppFlags>();
  
  // TypeScript will prevent typos and ensure flag exists
  const handleToggle = (flagName: FlagKey) => {
    toggleFlag(flagName); // ✅ Type-safe
  };
  
  // This would cause a TypeScript error:
  // toggleFlag('nonExistentFlag'); // ❌ Error
  
  return (
    <div>
      {flags.useNewHeader && <NewHeader />}
      <button onClick={() => handleToggle('showBetaFeatures')}>
        Toggle Beta Features
      </button>
    </div>
  );
}

TypeScript Benefits

🔍Auto-completion: IDE will suggest available flag names
Compile-time checks: Catch typos and errors before runtime
🔄Refactoring safety: Rename flags across your entire codebase
📚Self-documenting: Types serve as documentation

Best Practices

Tips for effective feature flag management

🎯 Flag Lifecycle

1. Introduction

Start with flag disabled, enable for specific users/environments

2. Gradual Rollout

Gradually increase exposure, monitor metrics and feedback

3. Full Deployment

Enable for all users once confidence is high

4. Cleanup

Remove flag and old code path after successful rollout

💡 Organization Tips

Do's

  • • Use descriptive, meaningful names
  • • Group related flags together
  • • Document flag purpose and timeline
  • • Set appropriate default values
  • • Regular flag cleanup and review

Don'ts

  • • Use generic names like 'flag1', 'temp'
  • • Leave obsolete flags in code
  • • Create flags for every small change
  • • Ignore flag performance impact
  • • Forget to remove flags after rollout

🚀 Performance Considerations

Minimal overhead: Flag checks are simple boolean operations
Optimized storage: Encrypted localStorage with efficient serialization
Avoid deep nesting: Keep flag logic simple and at component boundaries
Bundle size: Remove unused flag code paths during build process

Ready to implement feature flags?

Now that you understand configuration, let's explore the component APIs and see them in action.

Explore Components →