React 7,897 Compiler is a build-time tool — a Babel 41,624 plugin, MIT licensed, developed in the facebook/react repository — that inserts the memoization you would otherwise write by hand. Version 1.0 shipped on 7 October 2025 after a year in beta, and babel-plugin-react-compiler 1.0.0 is still the current release. It rewrites each component so that an expression is recomputed only when something it reads has changed. Your source does not change. This is an ordinary component:
function Hello({ name }) {
const onClick = () => alert(name);
return <button onClick={onClick}>Hi</button>;
}Running it through babel-plugin-react-compiler 1.0.0 with target: '19' produces this:
import { c as _c } from "react/compiler-runtime";
function Hello(t0) {
const $ = _c(2);
const {
name
} = t0;
let t1;
if ($[0] !== name) {
const onClick = () => alert(name);
t1 = <button onClick={onClick}>Hi</button>;
$[0] = name;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}_c(2) asks React for a two-slot cache that lives with this component instance, exactly like a Hook's state. Slot 0 holds the last name, slot 1 the last rendered <button>. When name is unchanged the whole element — handler included — is reused, so a parent re-render yields the identical element object and React skips the subtree: memo, useMemo and useCallback at expression granularity, with no dependency array to get wrong.
The compiler's conditions are strict, and that is the point: it optimizes only code that follows the Rules of React (Pure Rendering). Give it a component with a Hook inside an if and the output is byte-for-byte the input, with no _c import at all — it declines rather than risk changing behavior. Its limits follow from the same caution. It memoizes inside components and Hooks only, so a module-scope helper is never cached; it does not share a cache between components, so the same expensive call in two of them runs twice; and a changed context value still re-renders every reader, exactly as before.
So what still earns a Hook? React's guidance keeps useMemo and useCallback as escape hatches for values whose identity you must control yourself — most often one used as an Effect dependency, where you need certainty the Effect will not re-fire. memo still marks a boundary you have deliberately profiled. And in code that already has memoization, leave it: removing it changes what the compiler emits, so retire it against measurements.