How It Works: The Compilation Pipeline
Under the hood, React Compiler implements something remarkably sophisticated for a JavaScript tool: a proper compiler pipeline with intermediate representations, data-flow analysis, and multiple optimization passes.
Step 1: Parse to AST
The compiler starts as a Babel plugin (with swc support coming in Next.js 15.3.1+). It takes your component source code and converts it to an Abstract Syntax Tree.
Step 2: Build Control Flow Graph (CFG)
This is where it gets interesting. The compiler builds a Control Flow Graph — the same data structure used by optimizing compilers like LLVM and GCC. The CFG maps every possible execution path through your component.
// Your component:
function Product({ item, onAdd }) {
if (!item) return null;
const price = formatPrice(item.price);
return <Card price={price} onClick={() => onAdd(item.id)} />;
}
// CFG sees two paths:
// Path 1: item is falsy → return null
// Path 2: item is truthy → compute price → render Card
The CFG feeds into a High-Level IR where the compiler performs data-flow analysis. It tracks:
- What values depend on what inputs (props, state, context)
- Which operations are pure (no side effects)
- What can be safely cached between renders
This is where React Compiler does something manual memoization can't: it memoizes across conditional branches. Consider:
function ThemeProvider(props) {
if (!props.children) {
return null; // early return
}
// Manual useMemo CANNOT be placed here (Rules of Hooks)
// But React Compiler CAN memoize this computation
const theme = mergeTheme(props.theme, use(ThemeContext));
return (
<ThemeContext value={theme}>
{props.children}
</ThemeContext>
);
}
With useMemo, you'd have to restructure this component to avoid calling hooks after a conditional return. The compiler handles it automatically because it operates at the CFG level, not at the syntax level.
Step 4: Memoization Insertion
Based on the analysis, the compiler inserts memoization at the granularity of individual values and JSX elements. It doesn't just wrap your whole component in React.memo — it figures out exactly which pieces can be cached and which need to re-compute.
Step 5: Code Generation
The transformed code ships to the browser. It's still plain React — no special runtime needed. The memoization is expressed using React's existing primitives, just inserted automatically.