Quick take: - The React Compiler (
babel-plugin-react-compiler) statically analyzes your components at build time and inserts memoization automatically, the equivalent of correctly writtenReact.memo,useMemo, anduseCallbackwith accurate dependency arrays, across your entire codebase. This auto-memoization skips components that violate React's rules and tells you why. Works on React 17+. React DevTools shows exactly which components it covered, with practical examples.
The React Compiler is a build-time Babel plugin that reads your component code, figures out which values can be safely cached, and inserts the memoization for you. No component rewrites. No manual dependency arrays. You configure it once and it runs on every build.
I enabled it on a production Next.js app with around 200 components. The compiler optimized roughly 65% of them automatically. We removed about 40 manual useCallback and useMemo calls afterward, not because the compiler required it, but because they were now redundant and the compiler's coverage was verified through DevTools. That's the right rollout pace: enable, verify, clean up incrementally.
What Does the React Compiler Actually Do?
It's a static analysis pass. The compiler reads your component code, determines which computations and returned functions can be memoized without changing behavior, and rewrites the output to include those memo calls with correct dependency arrays.
// What you write
function ProductCard({ product, onAddToCart }) {
const price = formatPrice(product.price, product.currency);
const handleClick = () => onAddToCart(product.id);
return (
<div onClick={handleClick}>
<h3>{product.name}</h3>
<p>{price}</p>
</div>
);
}
// What the compiler generates (roughly)
function ProductCard({ product, onAddToCart }) {
const price = useMemo(
() => formatPrice(product.price, product.currency),
[product.price, product.currency]
);
const handleClick = useCallback(
() => onAddToCart(product.id),
[onAddToCart, product.id]
);
return (
<div onClick={handleClick}>
<h3>{product.name}</h3>
<p>{price}</p>
</div>
);
}
The dependency arrays are the part that's easy to get wrong by hand, and wrong dep arrays are where subtle stale-closure bugs live. The compiler gets them right because it has full visibility into what each function captures. For a refresher on why dep arrays matter, see React hooks best practices. This matters even more when components drive animations: unnecessary re-renders caused by unstable function references can break CSS transition timing and cause GSAP timelines to restart mid-sequence, a problem the CSS vs GSAP animation guide on Art of Styleframe covers in the context of scroll-linked and staggered animations.
How Do You Install the React Compiler?
npm install -D babel-plugin-react-compiler eslint-plugin-react-compiler
Next.js 15:
// next.config.ts
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;
Vite:
// vite.config.ts
import react from '@vitejs/plugin-react';
export default {
plugins: [
react({
babel: {
plugins: ['babel-plugin-react-compiler'],
},
}),
],
};
Plain Babel:
{
"plugins": ["babel-plugin-react-compiler"]
}
Add the ESLint plugin immediately, it catches patterns the compiler will refuse to optimize before you hit a runtime warning:
{
"plugins": ["eslint-plugin-react-compiler"],
"rules": {
"react-compiler/react-compiler": "error"
}
}
Don't skip the ESLint step. It surfaces the components you'll need to clean up before the compiler can touch them.
How Do You Verify It's Working?
React DevTools 5.x shows a "Memo" badge next to components the compiler has optimized. Open the Components panel after a development build, you'll see coverage immediately without any extra tooling.
You can also enable verbose output during development:
{
"plugins": [
["babel-plugin-react-compiler", {
"logger": "console"
}]
]
}
This logs a line per optimized component on startup. Noisy, but useful for a first-pass audit of what's covered. The React Compiler Playground lets you paste a component and see the compiled output before enabling it in your project, useful for understanding what the compiler will do to a specific pattern.
What Will the Compiler Skip?
The compiler skips components that violate React's rules. Common patterns that block optimization:
Mutating props or state during render:
// Skipped, mutates the array during render
function BadList({ items }) {
items.push({ id: 'temp' }); // compiler bails on the whole component
return <ul>{items.map(item => <li key={item.id}>{item.text}</li>)}</ul>;
}
Conditional hook calls:
// Skipped, hook inside a condition
function ConditionalHook({ isLoggedIn, userId }) {
if (isLoggedIn) {
const data = useUserData(userId); // whole component is skipped
}
}
Reading mutable external state without a subscription:
// Skipped, reads window.someGlobal which changes outside React's control
function GlobalReader() {
return <div>{window.someGlobal}</div>;
}
The compiler doesn't crash on these, it emits a warning and moves on to the next component. The ESLint plugin catches most of them at lint time before they reach the compiler.
Should You Remove Existing useMemo and useCallback?
Not all at once, and not without looking first. The compiler recognizes correct manual memoization and doesn't double-wrap it. But manual memoization written with incorrect dep arrays can interfere with the compiler's analysis.
My approach: enable the compiler, check DevTools for the "Memo" badges, then remove manual calls component by component where the compiler already has coverage. You'll occasionally find a manual dep array that was wrong, the compiler's correct version handles it, and you can see the difference in behavior.
For components the compiler skipped, fix the ESLint violations first. The fix is usually straightforward: move mutations out of render, un-conditionalize hook calls. Once the component is clean, the compiler handles the rest.
Don't try to fix everything at once. A team I consulted for ran the compiler on CI as an --emit-only check, used the DevTools report to track coverage over several sprints, and removed manual memoization as they went. That incremental approach worked better than a single large PR.
Summary
The React Compiler (babel-plugin-react-compiler) automates memoization at build time, correctly placed useMemo, useCallback, and React.memo across your codebase without manual dep arrays. Install it, add the ESLint plugin, enable it in your framework config, and check React DevTools to see coverage. It skips components that violate React's rules and warns you exactly which ones. Remove manual memoization incrementally after verifying DevTools coverage. The compiler works on React 17+, you don't need to be on React 19 first. For a full picture of what React 19 itself added, see our React 19 new features guide.
Related Tutorials
- Claude Code for React
- React 19: Complete Guide to New Features and Migration
- React data fetching
- React Hook Form vs Formik