Quick Start
Environment Setup
To begin contributing to or testing the React Compiler (internally referred to as "Forget"), you must configure a development environment capable of building the Babel plugin and running the High-level Intermediate Representation (HIR) passes.
Prerequisites
- Node.js: LTS version required.
- Yarn: The primary package manager for the React monorepo.
Installation
- Clone the repository and install dependencies:
git clone https://github.com/facebook/react.git cd react yarn install - Navigate to the compiler package:
cd compiler/packages/babel-plugin-react-compiler
Integrating the React Compiler
The compiler is implemented as a Babel plugin. It transforms standard React component code into an optimized version with automatic memoization of reactive scopes.
Configuration
The compiler's behavior is governed by the EnvironmentConfig type. Key parameters include:
hookPattern: A regex string used to identify custom hooks (e.g.,^use[A-Z]).validateNoCapitalizedCalls: A boolean flag to enforce JSX-only invocation for components.
Example Babel configuration:
{
"plugins": [
["babel-plugin-react-compiler", {
"hookPattern": ".*\\b(use[^$]+)$",
"validateNoCapitalizedCalls": true
}]
]
}
Static Analysis and Validation
The compiler performs several validation passes to ensure code adheres to React's architectural constraints.
Capitalized Call Validation
The ValidateNoCapitalizedCalls pass prevents components from being called as regular functions. Capitalized functions are reserved for components and must be invoked via JSX to ensure correct Hook execution contexts.
- Logic: The pass tracks
LoadGlobalandPropertyLoadinstructions. If a callee's identifier starts with an uppercase letter and is not in theALLOW_LIST(e.g.,Boolean,Number,String), the compiler throws aCapitalizedCallserror.
// Invalid: Throws CompilerError
function Component() {
const x = SomeFunc();
return x;
}
// Valid: Invoked via JSX
function Component() {
return <SomeFunc />;
}
Reactive Scope Optimization
A core feature of the compiler is the automatic pruning of non-escaping reactive scopes. This ensures that only values that impact downstream computation or the UI are memoized, reducing memory overhead.
Escape Analysis
The PruneNonEscapingScopes pass identifies identifiers that "escape" the local function scope. A value is considered escaping if:
- It is directly returned by the function.
- It is transitively aliased by a return value.
- It is passed as an argument to a hook (aliased by an external React-managed value).
Interleaved Dependencies
If a non-escaping value is part of an interleaved scope that produces an escaping value, the compiler may still memoize it to prevent cache invalidation of the primary output.
function Component(props) {
const a = [props.a]; // Non-escaping, but dependency of 'c'
const b = []; // Escaping output
const c = {};
c.a = a; // Aliasing 'a' into scope 'c'
b.push(props.b);
return b; // 'b' escapes, triggering memoization of 'a'
}
Running Tests and Fixtures
The compiler uses a fixture-based testing system located in src/__tests__/fixtures/compiler.
Executing the Test Suite
Run the following command within the compiler directory to execute all unit tests and validation passes:
yarn test
Adding Fixtures
To test specific compiler logic (e.g., memoization pruning), create a .js or .ts file in the fixtures directory. Use the FIXTURE_ENTRYPOINT constant to define the test case:
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: 42 }],
isComponent: true,
};
The test runner will automatically ingest the file, generate the HIR, apply optimization passes, and compare the output against the expected snapshot.