writing/blog/2026/06
BlogJun 22, 2026·6 min read

React Compiler 1.0: Automatic Memoization Guide

React Compiler 1.0 auto-memoizes components at build time, killing useMemo and useCallback boilerplate. A practical developer guide to install, config, and adopt it.

For a decade, making React fast meant sprinkling useMemo, useCallback, and React.memo across your components by hand. It was tedious, easy to get wrong, and impossible to apply in the places that needed it most. React Compiler 1.0 changes the default: the compiler reads your code and adds the right memoization for you, at build time.

This guide covers what React Compiler 1.0 actually does, how to install it in Vite and Next.js, the config options that matter, and how to adopt it on an existing codebase without breaking anything.

The problem manual memoization never solved

React re-renders a component whenever its state or props change. By default it also re-renders the entire subtree below it. To stop wasted work, developers reach for three tools:

  • useMemo to cache an expensive computed value
  • useCallback to keep a function reference stable between renders
  • React.memo to skip re-rendering a child when its props are unchanged

The pattern works, but it has real costs. You have to maintain dependency arrays by hand, and a stale or missing dependency is a classic source of bugs. You litter business logic with optimization noise. Worst of all, you cannot memoize after a conditional return — the Rules of Hooks forbid calling useMemo after an early return, which is exactly where a lot of expensive work lives.

React Compiler removes the guesswork. It analyzes data flow and mutability across each component, then inserts memoization automatically — including in places a human cannot, like after early returns, optional chains, and array index access.

What React Compiler 1.0 ships with

The 1.0 release, stabilized after years of battle-testing inside Meta, is the first stable cut. The headline numbers from Meta's production rollout:

  • Initial loads and cross-page navigations up to 12 percent faster
  • Specific interactions up to 2.5 times faster
  • Memory usage stays neutral

Beyond raw speed, 1.0 brings three things that matter for adoption. It is now part of create-vite, create-next-app, and create-expo-app templates, so new projects start optimized. Its lint rules ship inside eslint-plugin-react-hooks, encoding the Rules of React and catching bugs like calling setState during render. And there is an experimental SWC plugin path for Next.js that keeps builds fast without the full Babel pipeline.

Installing in a Vite project

React Compiler runs as a Babel plugin. Install it as an exact dev dependency — pinning the version is recommended until your test coverage is solid, because removing or changing memoization can subtly alter output.

pnpm add -D babel-plugin-react-compiler@latest

With @vitejs/plugin-react version 6 or later, wire it through the compiler preset:

// vite.config.js
import { defineConfig } from 'vite';
import react, { reactCompilerPreset } from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';
 
export default defineConfig({
  plugins: [
    react(),
    babel({
      presets: [reactCompilerPreset()],
    }),
  ],
});

If you configure Babel directly, the compiler plugin must run first in the plugin list:

// babel.config.js
module.exports = {
  plugins: [
    'babel-plugin-react-compiler', // must run first
    // ...other plugins
  ],
};

Installing in Next.js

Next.js wraps the compiler with a custom SWC optimization that only applies it to files containing JSX or Hooks, so builds stay fast. As of Next.js 16, the option is a top-level config flag. Install the Babel plugin, then flip it on:

npm install -D babel-plugin-react-compiler
// next.config.ts
import type { NextConfig } from 'next';
 
const nextConfig: NextConfig = {
  reactCompiler: true,
};
 
export default nextConfig;

That is the whole setup for a React 19 app. No dependency arrays to touch, no components to rewrite.

The config options that matter

Most React 19 apps need zero configuration. When you do need control, four options carry their weight.

compilationMode decides which functions the compiler touches. The default infers the right set. Set it to 'annotation' for opt-in adoption, where only functions you mark with the "use memo" directive get compiled:

// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-react-compiler', {
      compilationMode: 'annotation',
    }],
  ],
};

target tells the compiler which React version to emit for. The default is '19'. For React 17 or 18 apps, set it explicitly and add the runtime package:

['babel-plugin-react-compiler', {
  target: '18', // also: pnpm add react-compiler-runtime
}]

panicThreshold controls what happens when the compiler hits code it cannot safely optimize. Setting it to 'none' skips that component instead of failing the build — the recommended posture for production.

gating lets you put compiled output behind a runtime feature flag, so you can roll the compiler out to a percentage of users and compare before committing.

Adopting it on an existing codebase

The safe path on a mature app is incremental. Start in 'annotation' mode so nothing changes until you opt a component in with "use memo". Verify behavior, then widen the net.

You can also exclude any single component the compiler mishandles by adding the opt-out directive at the top of its body:

function LegacyChart() {
  "use no memo";
  // compiler skips this component
}

For your existing useMemo and useCallback calls, the guidance is conservative: leave them in place at first. Removing manual memoization can change the rendered output if the original code depended on a specific cache boundary. Once the compiler is on and your tests are green, you can delete the hand-written hooks gradually and let the compiler take over. For new code, rely on the compiler by default and reach for useMemo or useCallback only as deliberate escape hatches.

The other non-negotiable step is the linter. Install the latest eslint-plugin-react-hooks and use its recommended preset:

npm install -D eslint-plugin-react-hooks@latest

The compiler is only as good as the code it analyzes. The lint rules enforce the Rules of React that the compiler assumes — pure components, no mutation during render, stable Hook order — and surface violations before they reach the build.

Should you turn it on

If you are on React 19 and starting a new project, the answer is yes — it is already in the templates. For an existing app, the cost-benefit is strong: a measurable speedup, less optimization code to maintain, and a class of stale-dependency bugs eliminated. The risk is bounded because adoption is incremental and reversible per component.

The bigger shift is conceptual. Performance memoization moves from a manual discipline you carry in every code review to a build-time guarantee. As the React team put it, this stable release is meant to be "a new foundation and era for the next decade and more of React." For teams shipping React apps across the MENA region and beyond, that means faster experiences on lower-end devices and slower networks — with less code to write and maintain to get there.

Start with 'annotation' mode on one feature, measure, and expand. The compiler does the tedious part so you can spend your attention on the product.