System preferences

Back

SECTOR 02.1Transmission open

The compiler will remember for you

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:

CODE // TRANSMISSION13 LINES
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:

CODE // TRANSMISSION01 LINE
npm install -D babel-plugin-react-compiler@experimental eslint-plugin-react-compiler@experimental

eslint-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:

  1. New code: zero manual memoization. Write the naive version.
  2. Keep eslint-plugin-react-compiler green. It is the contract, enforced early.
  3. 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.