How React Compiler Works
React Compiler transforms React components through a multi-phase pipeline, converting your code into an optimized version with automatic memoization.High-Level Architecture
The compiler pipeline consists of seven major phases:Phase 1: HIR Construction
Lowering to HIR
The compiler first converts Babel’s Abstract Syntax Tree (AST) to a High-level Intermediate Representation (HIR). HIR represents code as a control-flow graph (CFG) with:- Basic blocks: Sequences of instructions
- Instructions: Individual operations
- Terminals: Control flow between blocks (if, return, loop, etc.)
SSA Conversion
The HIR is converted to Static Single Assignment (SSA) form, where:- Each variable is assigned exactly once
- Phi nodes represent values from different control flow paths
- Enables precise dataflow analysis
Phase 2: Optimization
Early optimization passes improve code quality:Constant Propagation
Replaces variables with known constant values:Dead Code Elimination
Removes unreferenced instructions:Phase 3: Type & Effect Inference
Type Inference
The compiler infers types using constraint-based unification:- Primitives (string, number, boolean, null, undefined)
- Objects (with shape information)
- Functions (including hooks)
- Arrays and JSX elements
Effect Inference
Infers aliasing and mutation effects through abstract interpretation: Effect Types:Phase 4: Reactive Scope Construction
Inferring Reactive Scopes
The compiler groups instructions that should invalidate together into reactive scopes:Scope Alignment
Scopes are aligned to:- Control flow boundaries (if/else, loops)
- Method call receivers
- Object method declarations
- Block scopes
Scope Merging
Overlapping or always-invalidating-together scopes are merged:Phase 5: HIR → Reactive Function
The control-flow graph is converted to a tree structure (ReactiveFunction) where:- Scopes become explicit nodes
- Dependencies are tracked
- Nesting relationships are preserved
Phase 6: Reactive Function Optimization
Pruning Passes
Multiple passes prune unnecessary scopes:- Non-escaping scopes: Values that don’t escape the function
- Scopes with hooks: Can’t memoize hook calls
- Always-invalidating: Scopes that always recompute
- Unused scopes: Empty or unreferenced scopes
Dependency Optimization
Phase 7: Code Generation
The final phase generates optimized JavaScript:Memoization Cache
Scope Structure
When the Compiler Bails Out
The compiler may bail out (skip compilation) when:Unsupported JavaScript Features
eval()callswithstatementsvardeclarations (uselet/const)- Nested
classdeclarations
Rule Violations
- Conditional hook calls
- Unconditional
setStateduring render - Direct
ref.currentaccess in render - Mutations after value is frozen
Complex Patterns
- Deeply nested closures capturing mutable state
- Dynamically created components
- Certain imperative patterns
Compiler Error Types
Todo Errors (Graceful Bailout)
Invariant Errors (Hard Failure)
Validation Errors
Performance Characteristics
Compilation Time
- Typically 1-5ms per component
- Scales linearly with code size
- Cached between builds
Runtime Overhead
- Memoization checks: O(1) per scope
- Cache storage: ~1-2 slots per reactive value
- Minimal memory overhead
Optimization Benefits
- Reduces re-renders by 50-90% in typical apps
- Eliminates need for manual memoization
- Improves performance on low-end devices
Next Steps
Architecture
Deep dive into compiler passes
HIR
Understanding the intermediate representation
Optimization Passes
Learn about specific optimizations
Configuration
Configure compiler behavior