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, notenable-google-signin) - Always strict
=== truecomparison (non-boolean andundefinedresolve tofalse)
Step 3: Create the Flag in GrowthBook
In GrowthBook:
- Create a feature with the exact same key (e.g.,
enable-my-feature) - Publish the feature
- Enable it for the desired environments
- 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
AppErrorif called outsideFeatureFlagContext
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:
- Remove the flag from
FeatureRegistryincore/registry.ts - Remove the UI mapping from
uiMapper.ts(and the field fromUIFeatureFlags) - Remove all usages in components and services
- (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:
- Add new flag with new key
- Migrate all usages to the new flag
- 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 |