The React Compiler is a build-time tool that reads plain, un-memoized component code and automatically inserts the same optimizations you used to write by hand with useMemo, useCallback, and React.memo. This lesson covers how it works, what it depends on, and how to adopt it in an existing codebase.
1Memoization Without the Boilerplate
Manual memoization with useMemo, useCallback, and React.memo works, but it requires developers to correctly identify what's expensive, wrap it, and maintain an accurate dependency array by hand โ a process that's easy to get subtly wrong. The React Compiler performs the same optimization automatically as part of the build, without any of that manual bookkeeping.
2What the Compiler Actually Does
Running as a build-time plugin, typically through Babel, the compiler statically analyzes each component and hook to determine which computed values and JSX subtrees actually depend on which inputs. It then rewrites the compiled output to skip recomputing anything whose relevant inputs are unchanged between renders โ the same principle behind useMemo, applied comprehensively and automatically.
3Installing and Enabling the Compiler
The compiler ships as babel-plugin-react-compiler alongside eslint-plugin-react-compiler, which flags patterns the compiler can't safely optimize. Frameworks like Next.js expose it behind a single configuration flag, after which it runs automatically on every build with no changes required to component source code.
4The Rules the Compiler Depends On
Automatic optimization is only safe for code that follows the Rules of React: components and hooks must be pure during render, and props or state must never be mutated in place. Code that breaks these rules, such as directly mutating a prop object, undermines the assumptions the compiler relies on, which is exactly what its accompanying ESLint rule is designed to catch before it causes a bug.
5Do You Still Need useMemo?
With the compiler enabled, most hand-written useMemo, useCallback, and React.memo calls become redundant and can be removed, which also eliminates a common source of stale-dependency-array bugs. Manual memoization still has a place for edge cases outside the compiler's analysis boundary, such as caching across module-level state the compiler doesn't track.
6Incremental Adoption
The compiler doesn't require an all-or-nothing migration โ it supports directory- or file-level opt-in, and a 'use no memo' directive placed at the top of a file explicitly excludes that component from compilation. This lets teams roll it out incrementally across a large, existing codebase rather than needing a single risky, big-bang migration.
7Step-by-Step Breakdown
Memoization Without the Boilerplate. You've already learned useMemo, useCallback, and React.memo โ powerful, but easy to misuse or forget. The React Compiler is a build-time tool that reads your component code and automatically inserts that memoization for you, so components stay fast without you having to hand-place a single dependency array.
What the Compiler Actually Does. The React Compiler is a build-time plugin (running through Babel or a similar toolchain) that statically analyzes your component and hook functions. It figures out which values and JSX subtrees genuinely depend on which inputs, then rewrites the compiled output to skip recomputing anything whose inputs haven't changed โ the same idea as useMemo, applied everywhere automatically.
Installing and Enabling the Compiler. The compiler ships as a Babel plugin (babel-plugin-react-compiler) plus an ESLint plugin (eslint-plugin-react-compiler) that flags code the compiler can't safely optimize. Most modern frameworks, including Next.js, let you enable it with a single config flag, after which it runs on every build without any changes to your component code.
What does the React Compiler's ESLint plugin do?
- โFlags code patterns the compiler can't safely auto-memoize
- โFormats code style like Prettier
The Rules the Compiler Depends On. The compiler can only safely optimize code that follows the Rules of React: components and hooks must be pure during render, and props/state must never be mutated directly. If your code breaks those rules โ for example, mutating a prop object in place โ the compiler either bails out of optimizing that component or, worse, produces incorrect behavior, which is exactly what the linter is there to catch.
Do You Still Need useMemo?. In most compiled codebases, no โ you can delete the vast majority of hand-written useMemo, useCallback, and React.memo calls and let the compiler handle it, which also removes a common source of stale-dependency bugs. You'll still reach for them manually in edge cases the compiler can't reason about, like memoizing a value across a boundary the compiler doesn't see, such as a module-level cache.
If the React Compiler is enabled project-wide, what's the recommended default approach to writing a new component?
- โWrite plain, readable code and let the compiler add memoization
- โManually wrap every value in useMemo just to be safe
Incremental Adoption. You don't have to enable the compiler across an entire legacy codebase at once. It supports directory-level or file-level opt-in through config, and a 'use no memo' directive lets you explicitly exclude a specific component the compiler is mishandling, so migration can happen incrementally, module by module.
Mastery Achieved. You now understand how the React Compiler works: static build-time analysis that inserts memoization automatically, why it depends on your components staying pure, why most manual useMemo/useCallback calls become unnecessary, and how to adopt it incrementally with 'use no memo'. Next, you'll see how React 19 and the compiler together change what 'modern rendering' looks like.
Level Up ๐
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Compilation happens at build time; runtime output works in all modern browsers.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Compiler Optimizations Don't Change Accessible Behavior
Automatic memoization only affects when computations re-run, not what gets rendered โ it doesn't change ARIA attributes, focus order, or semantic markup, so accessibility testing should continue independently of compiler adoption.
SEO Implications
- 1
Faster Interactivity Can Improve Core Web Vitals
By reducing unnecessary re-computation, compiler-optimized components can lower Interaction to Next Paint on client-heavy pages, which indirectly benefits search ranking signals tied to Core Web Vitals.
Best Practices
Fix ESLint Warnings Before Trusting Compiler Output
Treat eslint-plugin-react-compiler warnings as required fixes, not suggestions โ code that violates the Rules of React can silently produce incorrect optimized output rather than a build failure.
Remove Manual Memoization Gradually, Not All at Once
After enabling the compiler, prune redundant useMemo/useCallback calls incrementally and verify behavior, rather than deleting all manual memoization in a single large diff.
Frequent Bugs
A component behaves inconsistently after enabling the React Compiler, despite no visible code changes.
The component likely mutates props or state directly somewhere in its render path, violating the purity the compiler assumes. Run the compiler's ESLint rule to locate the mutation and refactor it to create new objects/arrays instead of mutating existing ones.
A specific legacy component crashes only after the compiler is enabled project-wide.
Add a 'use no memo' directive at the top of that file to exclude it from compilation while you refactor it to follow the Rules of React, then remove the directive once it's compliant.
Real-World Examples
Migrating a Heavily Memoized Dashboard
A dashboard component previously wrapped nearly every derived value in useMemo and every handler in useCallback to avoid re-render costs. After enabling the React Compiler and fixing two ESLint-flagged prop mutations, the team removed the manual memoization entirely and measured equivalent render performance with a smaller, more readable component.
// Before: manual memoization everywhere
const total = useMemo(() => sum(items), [items]);
const onSelect = useCallback((id) => setSelected(id), []);
// After: compiler handles it
const total = sum(items);
function onSelect(id) { setSelected(id); }