Overview
The React Compiler is a build-time tool that automatically memoizes components and hook values, removing most reasons to write useMemo, useCallback, and React.memo by hand. It reached v1.0 (stable) in October 2025. The point that trips up most teams: it is opt-in and decoupled from the React runtime version. A plain React 19 app does not get automatic memoization until you add the plugin. Calling it “the React 19 compiler” conflates the React major version with a separately versioned tool. This page covers what it does, how to enable it, and when manual memoization still pays. The umbrella rule set lives in react; the hook-level memoization rules live in react-hooks.
Treat the compiler as opt-in, not a runtime default
The compiler ships as a Babel plugin (babel-plugin-react-compiler), not as part of the React runtime. Installing React 19 does nothing to your render behavior until you wire the plugin into your build. Until then, every useMemo and useCallback you delete is a real regression, because nothing replaces the memoization. Decide the project’s stance explicitly: either enable the compiler and lean on it, or keep manual memoization disciplined.
Enable it through your build, then verify
In a framework, prefer the framework flag over a raw Babel config. In Next.js, set reactCompiler: true (shipped as a stable opt-in in Next.js 16). In a Vite or raw Babel app, add the plugin to the Babel pipeline.
// next.config.ts
export default {
reactCompiler: true,
};// babel.config.js (Vite / non-Next setups)
module.exports = {
plugins: [["babel-plugin-react-compiler", { target: "19" }]],
};Run eslint-plugin-react-hooks (which now bundles the compiler’s diagnostics) to find components the compiler had to bail out of. A component that breaks the rules of hooks or mutates props is skipped, not memoized.
Know what it memoizes and what it costs
The compiler analyzes each component and inserts memoization around values and JSX that would otherwise be recomputed on every render. It covers the cases teams used to handle with useMemo for derived values, useCallback for stable handlers, and memo() for child components with referentially stable props.
- Build-time cost: compilation adds work to every build; budget for it in CI.
- Runtime cost: the inserted memoization adds bookkeeping, which is a net win when renders are frequent and a wash when they are not.
- Correctness gate: the compiler only optimizes code that already follows the rules of hooks and avoids mutating props or state during render. Code that violates those rules is left untouched.
Keep manual memoization for the cases the compiler skips
Even with the compiler on, a few cases still call for a hand-written useMemo or useCallback, and without the compiler they remain the norm:
- A value passed to a non-React boundary (a third-party library, an imperative DOM API) that the compiler cannot see across.
- A computation the compiler bails out of because the component breaks a rule it cannot prove safe.
- A genuinely heavy computation where you want an explicit, reviewable memo regardless of compiler behavior.
Outside those cases, on a compiled codebase, write the plain expression and let the compiler handle it. See react-performance for the profile-first order of operations that decides whether any memoization is warranted at all.
Roll it out incrementally on an existing codebase
The compiler supports per-directory and per-file opt-in so you can adopt it gradually. Start by enabling it on a leaf feature folder, run the test suite and the profiler, and watch for behavior changes in components that were quietly relying on referential instability. Expand the scope once a slice is verified. Do not flip it on repo-wide and ship in the same change.