The React Compiler is a build step that memoizes your components for you.
Every useMemo, every useCallback, every memo() wrapper you have sprinkled across five years of components just became tech debt in waiting. The compiler is open source on the experimental channel, and it already runs in production on instagram.com and the Quest Store.
It is useMemo unrolled per expression
Skip the marketing, look at the output. Paste a component into the compiler playground and this comes back:
function Papers({ papers, filter }) {
const $ = _c(5)
let visible
if ($[0] !== papers || $[1] !== filter) {
visible = papers.filter((p) => p.tags.includes(filter))
$[0] = papers
$[1] = filter
$[2] = visible
} else {
visible = $[2]
}
// ...onOpen and the <List /> element get their own slots
}_c(n) (née useMemoCache) allocates a flat per-component cache array. Every intermediate value gets a numbered slot, and the compiler guards each slot with identity checks on exactly its inputs. The JSX elements get slots too. That is how child components skip re-rendering without a single memo() wrapper.
The deep dive talk by Gunasekaran and Zhang walks the path. The compiler lowers your component into a High-level Intermediate Representation in SSA form. It runs type inference plus mutability and aliasing analysis. That pass separates the values that can change between renders from the values that merely look like they can. Then it reconstructs JavaScript.
It tracks data flow, not syntax. That is why it memoizes correctly where humans memoize coarsely. Nobody hand-writes a dependency check per expression, and one unstable object prop from a lazy human voids a whole memo chain downstream. This tedium is exactly what compilers were invented for.
Three percent, bought by a build step
Meta showed the numbers at the React Conf keynote. instagram.com pages average ~3% faster with zero code changes. On the Quest Store, initial loads improved up to 12%, some interactions over 2.5x faster, memory flat. Three percent sounds small. It lands across every page of one of the largest React apps alive.
The linter is the real product
The compiler only optimizes code that follows the Rules of React: pure render, no mutation of props or state, hooks unconditional. The rules were always the deal. Now a machine reads the fine print. Break them and the compiler silently bails out per component, back to unmemoized reality. No error, no warning at runtime. Mutate a prop in render and that subtree quietly loses the optimization.
Two packages, and the second one matters first:
npm install -D babel-plugin-react-compiler@experimental eslint-plugin-react-compiler@experimentaleslint-plugin-react-compiler is not a style checker. It runs the compiler's own diagnostics. It reports every place the compiler gave up, and why: mutation during render, ref reads in render, conditional hooks. Run it before you adopt anything. It is a census of everywhere you have been lying to React. Some of those lies are your "un-debuggable stale value" bugs in disguise.
Keep your useMemo calls this month
The compiler tolerates them, and this is an experimental release. The policy forward writes itself:
- New code: zero manual memoization. Write the naive version.
- Keep
eslint-plugin-react-compilergreen. It is the contract, enforced early. - Delete the old ceremony once the compiler runs in your build. Lauren Tan's "Forget About Memo" is the migration sermon.
Five years of "memoize everything" versus "measure first" debates, and the answer was "neither, wait for the compiler". The machine reads your render function better than you do.