The Feature Flags System provides a centralized, type-safe mechanism for controlling runtime behavior across React Native's JavaScript, C++, and native platform code. It enables gradual rollout of new features, A/B testing, and platform-specific behavior customization through a single configuration file that generates synchronized implementations across all target languages.
This document covers the configuration, code generation, provider pattern, and usage patterns for feature flags. For information about how feature flags integrate with the New Architecture (Fabric/TurboModules), see Core Architecture and Runtime.
The feature flags system consists of four main components: a configuration file as the single source of truth, a code generation pipeline that produces synchronized implementations across languages, a provider pattern for customization, and accessor classes that provide thread-safe cached access.
Sources:
All feature flags are defined in a single configuration file that serves as the source of truth. The file exports a FeatureFlagDefinitions object with two categories: common flags (available in both JavaScript and native code) and jsOnly flags (JavaScript-only).
Flag Definition Schema:
| Field | Type | Description |
|---|---|---|
defaultValue | boolean | number | Default value returned when no provider override exists |
metadata.description | string | Human-readable description of the flag's purpose |
metadata.purpose | 'experimentation' | 'operational' | 'release' | Classification of flag intent |
metadata.dateAdded | string (optional) | Date when flag was introduced (YYYY-MM-DD) |
metadata.expectedReleaseValue | boolean | number | Expected value when feature is fully released |
ossReleaseStage | 'none' | 'experimental' | 'canary' | 'stable' | Current OSS rollout stage |
skipNativeAPI | boolean (optional) | If true, flag is not exposed to native code |
Example Configuration:
Sources:
The feature flags system uses a code generation script that reads the configuration file and produces synchronized implementations across all target languages. The script must be run manually whenever flags are added, removed, or modified.
Generation Command:
Generated File Locations:
| Language | Component | Path |
|---|---|---|
| JavaScript | Getters | src/private/featureflags/ReactNativeFeatureFlags.js |
| JavaScript | Base Helpers | src/private/featureflags/ReactNativeFeatureFlagsBase.js |
| JavaScript | TurboModule Spec | src/private/featureflags/specs/NativeReactNativeFeatureFlags.js |
| C++ | API | ReactCommon/react/featureflags/ReactNativeFeatureFlags.h/cpp |
| C++ | Accessor | ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h/cpp |
| C++ | Provider | ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h |
| C++ | Defaults | ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h |
| C++ | TurboModule | ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h/cpp |
| Kotlin/Java | API | ReactAndroid/.../internal/featureflags/ReactNativeFeatureFlags.kt |
| Kotlin/Java | Accessors | ReactAndroid/.../internal/featureflags/ReactNativeFeatureFlags{Cxx,Local}Accessor.kt |
| Kotlin/Java | Provider | ReactAndroid/.../internal/featureflags/ReactNativeFeatureFlagsProvider.kt |
| Kotlin/Java | Defaults | ReactAndroid/.../internal/featureflags/ReactNativeFeatureFlagsDefaults.kt |
| JNI | Interop | ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h/cpp |
Sources:
The feature flags system uses the provider pattern to allow customization of flag values. Each platform implements an abstract provider interface with default values, and applications can override the provider before runtime initialization.
Each platform defines a provider interface with a method for each feature flag:
C++ Provider Interface:
Kotlin Provider Interface:
Default Provider Implementation:
The default provider returns the defaultValue specified in the configuration file:
Sources:
Feature flags are accessed through language-specific APIs that provide typed getter functions. All implementations use lazy initialization and caching for performance.
JavaScript provides both JS-only flags and common flags that may call into native code:
Implementation Details:
JS-only flags use createJavaScriptFlagGetter() which reads from a local cache and checks for overrides:
Common flags use createNativeFlagGetter() which calls the native TurboModule on first access:
Sources:
C++ code accesses flags through static methods on the ReactNativeFeatureFlags class:
Implementation Flow:
getAccessor() to retrieve the global accessor singletonstd::atomic<std::optional<T>> per flag)currentProvider_->flagName()Sources:
Android code accesses flags through static methods on the ReactNativeFeatureFlags Kotlin object:
Accessor Implementation:
By default, the Kotlin API uses ReactNativeFeatureFlagsCxxAccessor which bridges to C++ via JNI:
The JNI interop calls into C++:
Sources:
Applications can override default flag values by providing a custom provider implementation before React Native runtime initialization. Overrides must be set before any flags are accessed, as the system caches values on first read.
JavaScript overrides are set using the override() function exported from ReactNativeFeatureFlags:
Important Constraints:
override()Sources:
C++ overrides are set by creating a custom provider and calling ReactNativeFeatureFlags::override():
Safety Mechanisms:
The accessor tracks which flags have been accessed via accessedFeatureFlags_ array. Attempting to override after accessing flags will trigger an error in debug builds:
Sources:
Android applications override flags by providing a custom provider to ReactNativeFeatureFlags.override():
Two Accessor Modes:
The Kotlin implementation supports two accessor modes:
CxxAccessor (default): Bridges to C++ via JNI. Overrides must be set in C++ via JReactNativeFeatureFlagsCxxInterop.override().
LocalAccessor: Pure Kotlin implementation for testing. Overrides work entirely in JVM without crossing JNI boundary.
Sources:
The feature flags system implements thread-safe caching to ensure consistent values and optimal performance across concurrent access.
C++ uses std::atomic<std::optional<T>> for each flag's cache entry:
Access Pattern:
Thread Safety Properties:
Sources:
Kotlin uses nullable cached variables with synchronized access patterns:
Thread Safety Notes:
Sources:
For testing scenarios, the system provides methods to reset state, though these are explicitly marked as dangerous:
C++:
Kotlin:
Warning: These methods can introduce consistency issues and should only be used in test environments where the React Native runtime is fully torn down and recreated between calls.
Sources:
The feature flags system enables cross-language access through TurboModule integration, allowing JavaScript to read native flag values and vice versa.
The NativeReactNativeFeatureFlags TurboModule exposes each common flag as a method callable from JavaScript:
The JavaScript spec defines the interface:
Sources:
When a Kotlin provider is set via ReactNativeFeatureFlags.override(), the system wraps it in a C++ adapter that implements the C++ provider interface by calling back into Java/Kotlin via JNI:
This allows Kotlin/Java code to provide overrides that affect all layers, including C++ and JavaScript.
Sources:
Each flag includes metadata that documents its purpose, lifecycle stage, and expected behavior when fully released. This metadata aids in flag management and cleanup.
| Field | Purpose |
|---|---|
description | Human-readable explanation of what the flag controls |
purpose | Classification: experimentation, operational, or release |
dateAdded | When the flag was introduced (YYYY-MM-DD format) |
expectedReleaseValue | The value this flag should have when the feature is complete |
The ossReleaseStage field tracks rollout status in open-source React Native:
| Stage | Meaning |
|---|---|
none | Not available in OSS; internal only or experimental |
experimental | Available but unstable; may change without notice |
canary | Enabled by default in canary/next releases |
stable | Enabled by default in stable releases |
Example Flag with Full Metadata:
This indicates:
defaultValue: false)purpose: 'release')expectedReleaseValue: true)ossReleaseStage: 'canary')Sources:
The React Native Feature Flags System provides:
ReactNativeFeatureFlags.config.js defines all flags with metadataTo add a new feature flag:
ReactNativeFeatureFlags.config.jsyarn featureflags --update to generate codeSources:
Refresh this wiki