facebook/react
Configuration
The runtime is configured along two axes: release channels (which set of feature flags is enabled) and bundle types (development vs production, ESM vs CJS vs UMD, etc.). Both are coordinated by scripts/rollup/build.js.
Release channels
A release channel is a per-build override of packages/shared/ReactFeatureFlags.js. Each channel has its own fork file in packages/shared/forks/ that re-exports the same flag names with potentially different defaults. The Rollup build picks the right fork based on the channel currently being built.
| Channel | Forks file | Notes |
|---|---|---|
stable |
(uses ReactFeatureFlags.js directly) |
What ships as react@latest on npm. |
experimental |
ReactFeatureFlags.js flags gated on __EXPERIMENTAL__ |
What ships as react@experimental on npm. |
next-major |
(transient experimental subset) | Used for testing the next major version's behavior. |
www-classic |
packages/shared/forks/ReactFeatureFlags.www.js (with disableLegacyMode = false) |
Facebook's legacy synchronous-mode codepath. |
www-modern |
packages/shared/forks/ReactFeatureFlags.www.js (with disableLegacyMode = true) |
Facebook's concurrent-mode codepath. |
www-dynamic |
packages/shared/forks/ReactFeatureFlags.www-dynamic.js |
Variant of www where some flags are evaluated at runtime via Facebook's gating system. |
test-renderer |
packages/shared/forks/ReactFeatureFlags.test-renderer.js |
What react-test-renderer runs against. |
test-renderer.www |
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js |
Test renderer with www flags. |
test-renderer.native-fb |
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js |
Test renderer with native flags. |
native-oss |
packages/shared/forks/ReactFeatureFlags.native-oss.js |
React Native open-source channel. |
native-fb |
packages/shared/forks/ReactFeatureFlags.native-fb.js |
React Native FB-internal channel. |
native-fb-dynamic |
packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js |
Native FB with runtime gating. |
eslint-plugin.www |
packages/shared/forks/ReactFeatureFlags.eslint-plugin.www.js |
The eslint plugin's view of www flags. |
readonly |
packages/shared/forks/ReactFeatureFlags.readonly.js |
Shim for tools that import flags but should never see real values. |
The yarn flags script (= node ./scripts/flags/flags.js) prints a matrix of every flag and its value per channel.
Feature flags
packages/shared/ReactFeatureFlags.js is the single source of truth for what flags exist. The file is organized into thematic sections, each commented with a name like:
- Land or remove (zero effort) — flags ready to be deleted or made permanent.
- Killswitch — flags that exist purely as escape hatches; should be removed once a feature has shipped without regressions.
- Slated for removal in the future (significant effort) — abandoned experiments waiting for callers to migrate.
- Ongoing experiments — features actively being developed.
- Profiling — flags controlling perf instrumentation.
Examples of long-running flags (as of this snapshot):
export const disableSchedulerTimeoutInWorkLoop = false;
export const enableSuspenseCallback = false;
export const enableScopeAPI = false;
export const enableCreateEventHandleAPI = false;
export const enableLegacyFBSupport = false;
export const enableYieldingBeforePassive = false;
export const enableThrottledScheduling = false;
export const enableLegacyCache = __EXPERIMENTAL__;
export const enableAsyncIterableChildren = __EXPERIMENTAL__;
export const enableTaint = __EXPERIMENTAL__;The pattern = __EXPERIMENTAL__ is shorthand for "on in experimental, off in stable". The Rollup replace plugin substitutes __EXPERIMENTAL__ with a literal true or false per channel, so the dead branch is dead-code-eliminated.
Lifecycle of a flag
- Add in
ReactFeatureFlags.jswith defaultfalse. - Gate all new code on it.
- Test both branches with
@gate(see how-to-contribute/testing). - Flip in experimental by setting it to
trueinReactFeatureFlags.js(under the= __EXPERIMENTAL__shorthand) or in the experimental fork. - Bake for several weeks of nightly prereleases.
- Flip in stable by hardcoding to
trueinReactFeatureFlags.js. - Bake for at least one stable release.
- Delete the flag and its dead branch.
A surprising number of flags spend years in steps 4–6.
Globals injected at build time
scripts/rollup/build.js configures Rollup's replace plugin to substitute these strings:
| Global | Substituted with |
|---|---|
__DEV__ |
true in development bundles, false in production. |
__PROFILE__ |
true in profiling bundles, false otherwise. Profiling bundles are dev bundles with Performance API instrumentation kept in. |
__EXPERIMENTAL__ |
true in the experimental channel, false elsewhere. |
__VARIANT__ |
true in variant builds (rarely used). |
__TEST__ |
true during Jest runs. |
process.env.NODE_ENV |
"development" or "production" per build. |
__DEV__ is by far the most common — every warning and console.error dev-only message is wrapped in if (__DEV__) { ... } so the production bundle dead-code-eliminates them.
Bundle types
scripts/rollup/build.js produces bundles in many shapes. The relevant constants are at the top of the file. Common types:
| Type | Purpose |
|---|---|
UMD_DEV / UMD_PROD |
UMD bundles for <script> tags (e.g. unpkg.com/react). |
NODE_DEV / NODE_PROD |
CommonJS bundles for Node.js. The default for the npm package. |
NODE_PROFILING |
Production CJS with profiling APIs preserved. |
ESM_DEV / ESM_PROD |
Native ESM bundles. |
BUN_DEV / BUN_PROD |
Bun-targeted bundles. |
FB_WWW_DEV / FB_WWW_PROD / FB_WWW_PROFILING |
Facebook internal bundles. |
RN_OSS_DEV / RN_OSS_PROD / RN_OSS_PROFILING |
React Native open-source bundles. |
RN_FB_DEV / RN_FB_PROD / RN_FB_PROFILING |
React Native Facebook-internal bundles. |
NODE_ES2015 |
A modern-syntax CJS bundle, used by the react-server condition path. |
Each bundles[].bundleTypes array in scripts/rollup/bundles.js declares which of these to produce.
Versioning
ReactVersions.js at the repo root is the source of truth for per-package versions. The file declares each package's version and its prerelease channel suffix. The script scripts/tasks/version-check.js (= yarn version-check) verifies that every packages/<pkg>/package.json agrees with this central file.
packages/shared/ReactVersion.js is generated from ReactVersions.js at build time and is what the published bundles import for React.version.
JSX configuration
The JSX runtime is built per-channel as part of react. The default Babel config at the repo root is babel.config.js. Variants:
babel.config-ts.js— TypeScript-specific config used by the compiler workspace.babel.config-react-compiler.js— config used when compiling fixtures with the React Compiler.
react/jsx-runtime, react/jsx-dev-runtime, and their .react-server.js siblings are tiny shims that re-export from packages/react/src/jsx/.
Lint and Flow configuration
.eslintrc.js— ESLint config, including the React-internal plugin inscripts/eslint-rules/..eslintignore,.prettierignore— ignored paths..prettierrc.js— Prettier config (Prettier 3 default + a couple of repo-wide overrides).flow-typed.config.jsonandflow-typed/— Flow library definitions.scripts/flow/createFlowConfigs.js— generates per-renderer.flowconfigfiles atyarn installtime.
Per-package package.json configuration
Most packages declare a long exports map with a react-server condition:
{
"name": "react",
"exports": {
".": {
"react-server": "./react.react-server.js",
"default": "./index.js"
},
"./jsx-runtime": {
"react-server": "./jsx-runtime.react-server.js",
"default": "./jsx-runtime.js"
}
}
}The react-server condition is what lets react/react-dom expose a different surface in RSC contexts. Every public package's package.json is templated by packages/<pkg>/npm/ — the npm/ directory is what gets actually published, with versions and dependencies rewritten by scripts/release/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.