Usage

Prev Next

Feature Flags Usage

This document describes how to add, expose, and delete feature flags in the platform.

Adding a New Feature Flag

Adding a feature flag is a code change first, not a GrowthBook change.

Step 1: Declare the Flag in the Registry

All feature flags must be declared in src/lib/external/FeatureFlag/core/registry.ts:

export const FeatureRegistry = {
  // ... existing flags

  'enable-my-feature': {
    key: 'enable-my-feature',
    default: false, // safe default when provider is unavailable
    owner: 'my-team', // team that owns this flag
    lifecycle: 'release', // release | capability | experiment | kill
    exposure: 'server', // server | client
    description: 'Short description of the feature',
  },
} satisfies FeatureFlagRegistry;

Lifecycle options:

Value Use Case
release Gradual rollout, intended to eventually become permanent
capability Long-lived toggle for optional platform capability
experiment A/B test or experiment, temporary by nature
kill Emergency kill switch — forced to boolean, always wins

Exposure options:

Value Meaning
server Evaluated server-side only. Use for security, infrastructure, or capability flags
client Safe to hydrate and read on client. Use for cosmetic experiments, UX variations

Step 2: Expose the Flag to the UI (if needed)

Only expose flags to the UI if UI behavior needs to change. Add a mapping in src/lib/external/FeatureFlag/runtime/uiMapper.ts:

export interface UIFeatureFlags {
  // ... existing flags
  showMyFeature: boolean;
}

export function mapToUIFeatures(flags: ResolvedFeatures): UIFeatureFlags {
  return {
    // ... existing mappings
    showMyFeature: flags['enable-my-feature'] === true,
  };
}

Rules for UI flag names:

  • Intent-based (what the UI should do, not what the infrastructure flag is called)
  • UX-oriented (showGoogleSignin, not enable-google-signin)
  • Always strict === true comparison (non-boolean and undefined resolve to false)

Step 3: Create the Flag in GrowthBook

In GrowthBook:

  1. Create a feature with the exact same key (e.g., enable-my-feature)
  2. Publish the feature
  3. Enable it for the desired environments
  4. Add targeting rules if needed (env, siteId, userId)

GrowthBook can only influence flags declared in code. If the key doesn't match, it has no effect.

Consuming Feature Flags

In React Components (Client)

Use the useFeatureFlagContext() hook from src/context/FeatureFlagContext.tsx:

import { useFeatureFlagContext } from '@/context/FeatureFlagContext';

function MyComponent() {
  const { showGoogleSignin } = useFeatureFlagContext();

  if (showGoogleSignin) {
    return <GoogleSignInButton />;
  }

  return null;
}

Components:

  • Only consume UIFeatureFlags — never raw infrastructure keys
  • The hook throws AppError if called outside FeatureFlagContext

In Server Components / Server Logic

Feature flags are fetched via FeatureFlagService:

import { FeatureFlagService } from '@/services/FeatureFlagService';

const flags = await FeatureFlagService.list();

if (flags.enableParallelChunkProcessing) {
  // enable server-side behavior
}

FeatureFlagService.list() calls GET /api/platform/features internally and returns UIFeatureFlags. On error, it returns {} and logs the error.

In the Root Layout

The root layout (src/app/layout.tsx) fetches flags and provides them to the entire app:

const featureFlags = await FeatureFlagService.list();

<FeatureFlagContext features={featureFlags}>
  {children}
</FeatureFlagContext>

Deleting a Feature Flag

Deletion is a code cleanup operation:

  1. Remove the flag from FeatureRegistry in core/registry.ts
  2. Remove the UI mapping from uiMapper.ts (and the field from UIFeatureFlags)
  3. Remove all usages in components and services
  4. (Optional) Archive or delete the flag in GrowthBook

After deletion:

  • The flag no longer exists for the platform
  • GrowthBook configuration becomes inert
  • No coordination with external tools is required

Renaming a Feature Flag

Treat renaming as:

  1. Add new flag with new key
  2. Migrate all usages to the new flag
  3. Delete old flag

Avoid in-place renames — they cause silent breakage if GrowthBook and code get out of sync.

Context and Targeting

Feature flags are resolved based on context attributes from cookies:

Attribute Source Available
env process.env.NODE_ENV Always
userId id cookie After auth
siteId siteId cookie After auth
site site cookie After auth

Missing attributes cause GrowthBook targeting rules to not match, resulting in default values — which is the intended safe behavior.

Environment Configuration

The feature flag API route requires an accessible base URL when called from server components:

NEXT_PUBLIC_PLATFORM_BASE_URL=https://<domain>

GrowthBook requires:

GROWTHBOOK_API_HOST=https://cdn.growthbook.io
GROWTHBOOK_CLIENT_KEY=sdk-xxxxx

If GrowthBook env vars are missing, the system silently falls back to the NoopFeatureProvider and uses registry defaults.

Things to Avoid

Anti-pattern Why
Create flags only in GrowthBook Flag won't exist for the platform — registry is the source of truth
Access raw flag keys in UI Couples UI to infrastructure naming; use UIFeatureFlags via mapper
Use flags for authorization Use the Permission class for access control
Use flags for billing/plan enforcement Flags are advisory, not authoritative
Leave obsolete flags undeleted Accumulation makes the registry untrustworthy
Rename flags in-place Causes silent breakage between code and GrowthBook