React Compiler

The React Compiler is a build-time tool that automatically optimizes React components and hooks. It analyzes your code and inserts fine-grained memoization, the work developers used to do by hand with React.memo, useMemo, and useCallback. The goal: apps that re-render only what actually changed, without cluttering code with memoization hooks and dependency arrays.

The compiler reached a stable 1.0 release in 2025 after being used in production at Meta. It works with React 17, 18, and 19, and integrates with Babel-based toolchains, Vite, Next.js, and Expo. It relies on your code following the Rules of React; where it can't prove a component is safe, it simply skips optimizing it.

TL;DR

Quick Example

Without the compiler, preventing wasted renders means manual memoization:

With the compiler, you write the plain version:

Enabling it in a Vite project:

And in Next.js:

Core Concepts

What the Compiler Does

The compiler transforms each component and hook into a version that caches intermediate results in slots keyed by their inputs. When a component re-renders:

It memoizes at a finer granularity than typical hand-written code, including individual JSX subtrees and values inside conditionals, which is awkward or impossible to do manually because of the Rules of Hooks.

The Rules of React

The compiler's optimizations are only valid if components behave like pure functions of props, state, and context:

When the compiler detects a violation it can't reason about, it bails out of optimizing that component and leaves it unchanged, rather than producing broken code. Code that breaks the rules subtly (mutations the compiler can't see) can still misbehave, which is why the lint rules matter.

Linting

The compiler's diagnostics ship as rules in eslint-plugin-react-hooks (the recommended config in recent versions). They flag rule violations such as mutations during render, reading ref.current during render, and setState in render, even if you haven't enabled the compiler yet. Fixing these makes code more correct regardless.

Incremental Adoption

Many teams enable it for the whole app once lint errors are cleared, then use "use no memo" for rare exceptions.

What Happens to useMemo and useCallback?

Remove manual memoization gradually and verify with the Profiler, rather than stripping it all at once. See React performance.

Best Practices

Fix Lint Errors First

Enable the compiler-related ESLint rules before turning on the compiler. They surface code patterns that would cause bailouts or bugs, and they're worth fixing anyway.

Verify With React DevTools

Components optimized by the compiler show a "Memo ✨" badge in React DevTools. Use it, together with the Profiler, to confirm important components are compiled and re-rendering less.

Keep Components Pure

Write components as pure functions of props and state, put side effects in effects or event handlers, and treat data as immutable. That's good React regardless, and it's what lets the compiler optimize everything.

Test Behavior, Not Render Counts

Since the compiler changes when components re-render, tests that assert exact render counts or rely on side effects during render can break. Test user-visible behavior with tools like React Testing Library.

Common Mistakes

Mutating Props or State

Reading Refs During Render

if (renderCount.current > 3) inside render makes output depend on a mutable ref the compiler doesn't track. Refs are for event handlers and effects; use state for values that affect rendering.

Assuming the Compiler Fixes Everything

The compiler reduces unnecessary re-renders. It doesn't virtualize long lists, split bundles, fix data-fetching waterfalls, or speed up genuinely expensive computations. Those still need targeted solutions.

FAQ

Do I need React 19 to use the compiler?

No. It targets React 19 by default and supports React 17 and 18 with the react-compiler-runtime package and the target option. React 19 includes the needed runtime natively.

Will the compiler break my app?

It's designed to be conservative: code it can't prove safe is left unoptimized. Problems usually come from code that already violates the Rules of React in ways the compiler couldn't detect. Enable the ESLint rules, adopt incrementally, and run your test suite.

Should I remove all my useMemo and useCallback calls?

Not immediately. They're harmless alongside the compiler. Remove them gradually when touching code, keeping any that exist for correctness (stable identity for effect dependencies or external libraries) rather than performance.

Does the compiler work with other frameworks' build tools?

Yes. It's a Babel plugin, with integrations for Vite, Next.js, Expo/React Native (Metro), webpack/Rspack, and others. SWC-based pipelines like Next.js enable it through config, using the Babel plugin where needed.

Related Topics

References