If you have been writing React for any amount of time, you know the pain. A component re-renders too often, so you wrap a value in useMemo. A callback gets recreated on every render, so you reach for useCallback. A child component renders unnecessarily, so you wrap it in React.memo. And then you spend the next hour debugging why your memoization is not working.
React Compiler, which hit v1.0 in October 2025, solves this at the compiler level. It automatically analyzes your components and adds memoization where it is needed — without you writing a single useMemo or useCallback.
What the React Compiler Actually Does
React Compiler is a build-time tool that transforms your React code before it reaches the browser. It analyzes your components and hooks, identifies what values and functions can be memoized safely, and inserts the memoization automatically.
The key insight is that the compiler can see your entire component at once. It knows exactly when a value can change and when it cannot — something that is easy to get wrong when you are doing it manually.
Before the compiler:
function ProductList({ category, onSelect }) {
const filteredProducts = useMemo(
() => products.filter((p) => p.category === category),
[category]
);
const handleSelect = useCallback(
(id) => {
onSelect(id);
},
[onSelect]
);
return (
<ul>
{filteredProducts.map((product) => (
<ProductItem
key={product.id}
product={product}
onSelect={handleSelect}
/>
))}
</ul>
);
}
After the compiler (what you write):
function ProductList({ category, onSelect }) {
const filteredProducts = products.filter((p) => p.category === category);
const handleSelect = (id) => {
onSelect(id);
};
return (
<ul>
{filteredProducts.map((product) => (
<ProductItem
key={product.id}
product={product}
onSelect={handleSelect}
/>
))}
</ul>
);
}
The compiler produces equivalent output to the first version — but you write the second. Clean, readable, no ceremony.
How to Enable It
The React Compiler works as a Babel plugin or an SWC plugin. Most frameworks already have first-class support.
Next.js
// next.config.js
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
module.exports = nextConfig;
Vite (with the Babel plugin)
npm install babel-plugin-react-compiler
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: ['babel-plugin-react-compiler'],
},
}),
],
});
That is all the setup you need. The compiler runs at build time and optimizes your components automatically.
What the Compiler Optimizes
The compiler handles three major patterns that used to require manual work:
1. Expensive computations
Anything that is computationally heavy gets memoized automatically when the compiler determines the dependencies have not changed.
// You write this
function Dashboard({ data }) {
const summary = computeExpensiveSummary(data);
return <SummaryCard data={summary} />;
}
// The compiler treats it like this
function Dashboard({ data }) {
const summary = useMemo(() => computeExpensiveSummary(data), [data]);
return <SummaryCard data={summary} />;
}
2. Stable function references
Functions passed as props no longer cause unnecessary child re-renders.
// You write this
function Parent() {
const handleClick = () => console.log('clicked');
return <Child onClick={handleClick} />;
}
// Compiler ensures handleClick reference is stable across renders
3. Component memoization
React.memo is effectively applied automatically to components that receive stable props. You do not need to wrap anything manually.
The Rules: What the Compiler Requires
The compiler enforces the Rules of React strictly. If your code breaks the rules, the compiler either skips optimizing that component or errors out. The rules are not new — they have always existed — but the compiler makes violating them visible.
Key rules the compiler enforces:
- Components must be pure functions — same inputs, same output
- Hooks cannot be called conditionally
- State updates must not directly mutate state
If you have been writing React correctly, you likely have no issues. If your codebase has some legacy patterns, the compiler’s diagnostic output is genuinely helpful for finding them.
When You Still Need Manual Optimization
The compiler is smart, but it is not magic. There are cases where manual intervention still makes sense:
Extremely expensive operations
For operations like parsing large datasets or running complex algorithms, you might want explicit control over when the computation runs, regardless of what the compiler decides.
Virtualization
For long lists, react-window or react-virtual is still the right tool. The compiler does not handle virtualization.
Refs and imperative code
Anything involving useRef for DOM manipulation or imperative APIs is outside the compiler’s optimization scope.
Third-party libraries with unusual patterns
Some older libraries have non-standard patterns the compiler cannot analyze safely. It will skip those and leave them as-is.
Gradual Adoption with use no memo
If you have an existing codebase and want to adopt the compiler incrementally, you can opt individual components out using a directive:
function LegacyComponent() {
'use no memo';
// This component will not be optimized by the compiler
// Useful during migration
}
This lets you enable the compiler project-wide and opt out specific components that you have not reviewed yet, rather than doing a big-bang migration.
Does This Make React Faster?
In practice, yes — but not dramatically for simple apps that were already written well. The biggest wins are in:
- Large component trees where manual memoization was incomplete or missing
- Codebases with legacy components that never had optimization added
- Teams with mixed experience levels where memoization was inconsistently applied
If you are on a well-optimized codebase and you profiled everything carefully, you will see modest improvement. If you are on a typical production app with inconsistent useMemo and useCallback usage, you will see real gains — and your code will be cleaner.
My Take
The React Compiler solves a real problem. Memoization in React was always a manual, error-prone process that rewarded developers who understood the internals and punished everyone else. Moving it to the compiler is the right call.
More importantly, it cleans up the code. The amount of useMemo, useCallback, and React.memo boilerplate in most React codebases is genuinely embarrassing. The compiler lets you write straightforward code and trust that the framework handles the performance details.
The migration story is solid with opt-out directives, and framework support is already there for Next.js and Vite. If you are starting a new React project in 2026, enable the compiler from day one.
Conclusion
React Compiler v1.0 automates one of the most tedious parts of writing performant React. You write simple, readable components. The compiler figures out what to memoize and does it for you.
Enable it in your build config, let it run, and resist the urge to add useMemo back “just in case.” The compiler knows what it is doing.