Writing Technical guide

Web Development · 4 min read

React Compiler Changes When to Reach for useMemo

React Compiler 1.0 moved routine rendering memoization into the build. React.memo, useCallback, and useMemo now need a specific reason.

In this article4 sections

React Compiler 1.0 changes the default. React.memo, useCallback, and useMemo no longer need to appear automatically in every performance-minded component. The compiler handles routine rendering memoization during the build and can do it more granularly than a hand-written wrapper.

That does not make the manual APIs obsolete. It means I want a specific reason before I use one.

Before and After

Before: developers wrapped components in React.memo, dependencies in useCallback, and expensive calculations in useMemo, then maintained the dependency arrays by hand.

After: the compiler analyzes the code and memoizes values used in rendering. It can apply that memoization conditionally.

The compiler can cache values calculated during rendering and avoid repeated component work when its inputs have not changed. It does not rewrite arbitrary callbacks received through props into a promise of stable identity.

The component stays easier to read, and the compiler can find opportunities that would be tedious to maintain by hand. It is the same reason I prefer TypeScript to comments that promise a value has the right shape: let the tool enforce what it can.

When You Still Need Manual Memoization

Check the compilation target One place: useEffect dependencies. If you pass a value to useEffect and want to control when it runs, wrap it in useMemo.

The official React blog explains this: manual memoization remains an escape hatch when effect dependencies need more precise control. It is still a performance cache, not a semantic guarantee: React may discard a memoized value. Prefer an effect expressed in terms of the primitive values it actually uses.

Setting It Up

React Compiler supports React 17 and later. React 17 or 18 also needs react-compiler-runtime and the matching compiler target; React 19 includes the runtime support.

Keep renders pure The compiler assumes your components follow the Rules of React (no side effects during render, stable hook ordering). Compiler-powered lint rules can report violations, and the compiler can skip unsupported components. Neither removes the need to test behavior.

With Next.js

Next.js 16 has a top-level reactCompiler option. Update your config:

// next.config.js
const nextConfig = {
  reactCompiler: true,
};

Next.js handles the Babel plugin setup automatically.

With Vite

Vite uses esbuild by default, so you add the Babel plugin manually. The trade-off: automatic memoization at the cost of Babel overhead in your build.

Install:

npm install babel-plugin-react-compiler@latest

Configure:

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: ["babel-plugin-react-compiler"],
      },
    }),
  ],
});

The React Compiler must run first in your Babel plugin pipeline. @vitejs/plugin-react handles this when configured as shown.

Measure the build tradeoff The Babel integration adds build work. Whether the runtime benefit pays for that cost depends on the application. If your codebase is large and build time is critical, benchmark both.

The Babel example above is the 2025 integration path. By August 2026, Oxc added React Compiler support, and VoidZero described its Vite 8 integration. For a new installation, check the current Vite React plugin instructions rather than assuming Babel is required.

A cautious adoption path

  1. Update to React 19+ (or 17/18 with react-compiler-runtime)
  2. Install the compiler: npm install babel-plugin-react-compiler@latest
  3. Configure it for the installed framework version
  4. Remove React.memo from components where it is not needed
  5. Test carefully before removing useCallback/useMemo
  6. Watch your bundle size and build times

The React Compiler installation guide has framework-specific setup for Next.js, Remix, Vite, and others. The Next.js 16 announcement covers Turbopack details.

I would remove memoization one component at a time and watch behavior, bundle size, and build time. The compiler can own routine rendering optimizations; manual memoization remains useful when reference stability is part of the component’s behavior.

That is the same threshold I use for React Router loaders and React Query: start with the smaller mechanism and let observed behavior earn the extra layer.

Was this useful?

What was missing?

Thanks for the feedback.