How to Adopt the React Compiler

This guide is part of React Rendering Performance, within Framework Performance. The React Compiler is a build-time tool that automatically memoizes React components and hooks. It analyses each component, works out which values depend on which inputs, and inserts caching so that re-renders reuse JSX, computed values and callbacks when their inputs have not changed. The effect is similar to wrapping everything carefully in React.memo, useMemo and useCallback — but more granular, consistent and without cluttering the code.

The compiler relies on components following the Rules of React: render must be pure, props and state must not be mutated, and hooks must be called unconditionally. Code that breaks these rules is skipped (the compiler bails out for that component) rather than miscompiled. Adoption is therefore mostly about enabling the compiler, finding and fixing violations with the ESLint plugin, and verifying the performance effect.

Compiler adoption path Steps for adopting the React Compiler in an existing codebase. Compiler adoption path Lint Rules of React plugin Fix violations in hot code Enable opt-in directory or annotation Verify Profiler + tests Expand whole app

Rapid Diagnosis

  • Check React version and build tool: the compiler integrates with Babel, Vite, Next.js and other toolchains; React 19 is the main target, with support for earlier versions via a runtime package.
  • Run the ESLint plugin for React Compiler rules to see how many violations exist.
  • Profile key interactions before enabling, to have a baseline.
  • Check manual memoization usage: heavy manual memoization indicates areas where the compiler will help most.

Root Cause Analysis: Why Apps Re-render Too Much

1. Missing memoization. Components re-render because parents do.

2. Unstable values. New objects and callbacks each render defeat memoization.

3. Inconsistent manual memoization. Some paths memoized, others not.

4. Rules violations. Mutations and impure renders make optimisation unsafe.

Step-by-Step Resolution

1. Install the lint rules and fix violations

bash
npm install -D eslint-plugin-react-compiler
# .eslintrc: plugins: ['react-compiler'], rules: { 'react-compiler/react-compiler': 'error' }
# trade-off: the lint rule flags code the compiler cannot optimise; fixing every
# warning at once is a large change, so start with performance-critical areas.

Typical violations: mutating props or state objects, reading refs during render, calling hooks conditionally, and side effects in render.

2. Enable the compiler incrementally

Enable it for a directory or with an opt-in annotation, then expand. In Next.js, the reactCompiler option in next.config enables it; in Vite, the Babel plugin babel-plugin-react-compiler.

javascript
// babel.config.js — compile only a subset at first.
module.exports = {
  plugins: [['babel-plugin-react-compiler', { sources: (filename) => filename.includes('src/features/search') }]],
};
// trade-off: partial adoption makes it easier to attribute regressions, but the
// biggest wins often come from shared components used everywhere.

3. Verify behaviour and performance

Run tests, then profile the same interactions. Components optimised by the compiler show a "Memo ✨" badge in React DevTools.

4. Remove redundant manual memoization gradually

Manual hooks keep working; remove them where the compiler handles the case, to simplify code.

Components rendered per keystroke in a search page Bar chart comparing components rendered per keystroke before and after enabling the React Compiler. Components rendered per keystroke in a search page No memoization 640 Manual memo (partial) 210 React Compiler 34

Verification

Check that optimised components show the compiler badge in React DevTools. Profile key interactions: fewer components should render, with shorter commits. Run the full test suite, paying attention to behaviour that relied on re-rendering (for example, code that read mutable values during render). In the field, compare INP before and after the rollout.

Worked Example: A Project Management App

A project management app with 1,200 components had inconsistent manual memoization. Typing in a task description re-rendered the board, sidebar and header: 85ms per keystroke on a throttled profile. The ESLint plugin reported 140 violations, mostly mutations of arrays in props and refs read during render. The team fixed the 60 violations in the board and editor modules, enabled the compiler for those modules, then expanded to the whole app over three weeks. Keystroke commits dropped to 9ms, board drag interactions from 140ms to 45ms, and INP p75 improved from 240ms to 150ms. About 300 manual useMemo/useCallback calls were later removed.

What the Compiler Does Not Do

The compiler reduces re-rendering work, but it does not make individual renders of large lists cheap, does not virtualize, does not split bundles, does not reduce hydration of components that must render, and cannot optimise components that break the rules. Expensive computations inside a component still run when their inputs change. Combine it with state colocation, virtualization, transitions for non-urgent updates, and server components for content that need not be interactive.

What the compiler handles vs what you still own Comparison of the optimizations the React Compiler applies automatically and those still needed manually. What the compiler handles vs what you still own Compiler handles • Memoizing JSX and components • Stable callbacks and computed values • Skipping unchanged subtrees You still own • Virtualizing long lists • Bundle size and hydration • Transitions for non-urgent updates • Fixing Rules of React violations

Rolling Out Safely

Treat the compiler rollout like a dependency upgrade with behavioural risk. Enable it in a branch, run the full test suite, and exercise flows that rely on refs, mutable objects or third-party libraries with unusual patterns. Ship to a share of users or an internal environment first and watch error rates as well as INP. Because the compiler only adds caching, correct code behaves the same; problems appear in code that was silently relying on re-rendering to pick up mutated values. When a component misbehaves, the "use no memo" directive opts it out while you fix the underlying issue.

Common Mistakes

  • Enabling everywhere at once without lint fixes. Many components silently skipped.
  • Assuming all components are optimised. Check the DevTools badge.
  • Relying on mutation during render. Breaks under compilation and concurrent rendering.
  • Removing manual memoization before verifying. Remove only where the compiler covers it.

Edge Cases

Third-party components. Libraries are not compiled unless they ship compiled code; props passed to them are still stabilised by your compiled components.

Class components. Not optimised by the compiler.

Refs during render. Reading ref.current during render prevents optimisation; read refs in effects or handlers.

Older React versions. Use the compiler's runtime compatibility package and check documentation for supported versions.

FAQ

What does the React Compiler do?

It automatically memoizes components and hooks at build time, so re-renders skip work when inputs have not changed.

Do I need to rewrite my code?

Only code that breaks the Rules of React. Most idiomatic React code compiles without changes.

How do I know a component was compiled?

React DevTools shows a "Memo ✨" badge next to components optimised by the compiler.

Does the compiler increase bundle size?

Slightly, due to inserted caching code. The runtime savings typically outweigh it for interactive apps.

Can I use it with Next.js?

Yes, via the reactCompiler option in the Next.js configuration.

What happens with components that break the rules?

The compiler skips them and they behave as before, without automatic memoization.

Should I remove React.memo, useMemo and useCallback?

They are harmless with the compiler. Remove them gradually where the compiler covers the case, to simplify code.

Does the compiler help hydration?

Not much. Hydration renders every client component once regardless of memoization. Reduce hydration with server components and Suspense boundaries.

Is the React Compiler production-ready?

It has been used in large production apps. As with any build tool change, adopt it incrementally with tests and monitoring.

Will the compiler change how my components behave?

Not for code that follows the Rules of React. Components that mutate values during render may behave differently, which is why the lint step comes first.

How do I measure the compiler's impact in the field?

Compare INP p75 per route before and after the rollout, ideally with a staged release so both versions run in the same period.