I opened a PR last week and my senior dev left one comment:
"Why are you still writing useMemo? The compiler handles this now."
I paused. Closed my laptop. Stared at the wall.
Because I had spent two years carefully wrapping calculations in useMemo, obsessing over dependency arrays, and arguing in code reviews about when to use useCallback. And apparently... I didn't need to do most of it.
Here's everything you need to know about React Compiler — and yes, what you can actually delete. 🚀
What Even Is React Compiler?
For years, React performance optimization felt like a ritual nobody fully understood.
You'd sprinkle useMemo everywhere. Wrap callbacks in useCallback. Forget a dependency. Break something. Fix it. Add React.memo to a component. Argue with your team about whether it was necessary.
React Compiler ends this.
It's a build-time tool that analyzes your components and automatically applies memoization where it's needed — without you writing a single useMemo or useCallback.
The result? You write clean, simple React code. The compiler makes it fast.
React Compiler works on both React and React Native, and automatically optimizes components and hooks without requiring rewrites. The compiler has been battle tested on major apps at Meta and is fully production-ready.
The Real Numbers (Not Marketing Fluff)
Before I tell you what to delete, let's talk about what this actually does in production.
Real-world production metrics from Wakelet after rolling the compiler to 100% of users showed overall LCP improved by 10% (2.6s to 2.4s), and INP improved by about 15% (275ms to 240ms). For pure React elements like Radix dropdowns, the INP speedup was closer to 30%.
In the Meta Quest Store, initial loads and cross-page navigations improved by up to 12%, while certain interactions are more than 2.5× faster. Memory usage stays neutral even with these wins.
These aren't toy benchmarks. These are real apps with real users.
Before vs After — The Code That Changed Everything
Here's what we used to write:
// ❌ The old way — manual memoization everywhere
import { useMemo, useCallback, memo } from 'react';
const ProductList = memo(function ProductList({ products, onSelect }) {
const sortedProducts = useMemo(() => {
return products.slice().sort((a, b) => a.name.localeCompare(b.name));
}, [products]);
const handleSelect = useCallback((id) => {
onSelect(id);
}, [onSelect]);
return (
<ul>
{sortedProducts.map(product => (
<ProductItem
key={product.id}
product={product}
onSelect={handleSelect}
/>
))}
</ul>
);
});
Here's what you write now:
// ✅ The new way — just write React
function ProductList({ products, onSelect }) {
const sortedProducts = products
.slice()
.sort((a, b) => a.name.localeCompare(b.name));
const handleSelect = (id) => {
onSelect(id);
};
return (
<ul>
{sortedProducts.map(product => (
<ProductItem
key={product.id}
product={product}
onSelect={handleSelect}
/>
))}
</ul>
);
}
Same performance. Zero manual memoization. The compiler handles it all.
How to Enable React Compiler Today
For Vite projects:
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'],
},
}),
],
});
For Next.js 16+:
Even easier — just one line:
// next.config.ts
const nextConfig = {
reactCompiler: true,
};
export default nextConfig;
Next.js 16 has built-in stable support for React Compiler. Just enable it in your config with reactCompiler: true.
For Expo:
Expo SDK 54 enables the compiler by default in the project template — no configuration needed for new projects.
What You Can Actually Delete
Here's the part you came for. The honest answer:
✅ Safe to Delete (New Code):
1. useMemo for derived calculations
// ❌ Don't write this anymore
const filtered = useMemo(() => {
return items.filter(item => item.active);
}, [items]);
// ✅ Just write this
const filtered = items.filter(item => item.active);
2. useCallback for event handlers
// ❌ Don't write this anymore
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
// ✅ Just write this
const handleClick = () => {
doSomething(id);
};
3. React.memo wrappers
// ❌ Don't wrap new components in memo
const MyComponent = memo(function MyComponent({ data }) {
return <div>{data.name}</div>;
});
// ✅ Just write the component
function MyComponent({ data }) {
return <div>{data.name}</div>;
}
What You Should KEEP
Here's the honest part nobody tells you.
In some cases developers may need more control over memoization. The useMemo and useCallback hooks can continue to be used with React Compiler as an escape hatch to provide control over which values are memoized. A common use-case is when a memoized value is used as an effect dependency, to ensure that an effect does not fire repeatedly even when its dependencies don't meaningfully change.
Specifically, keep useMemo/useCallback when:
// Keep useCallback when used as useEffect dependency
const fetchData = useCallback(async () => {
const data = await api.fetch(userId);
setData(data);
}, [userId]);
useEffect(() => {
fetchData();
}, [fetchData]); // ← stable reference needed here
// Keep useMemo for external library integrations
// that require stable object identity
const chartConfig = useMemo(() => ({
type: 'line',
data: chartData,
options: { responsive: true }
}), [chartData]);
// chartConfig passed to a third-party chart library
// that does reference equality checks internally
The One Thing That Can Go Wrong
React Compiler works best when the auto-memoization applied is strictly for performance. Future versions may change how memoization is applied. However, because product code may sometimes break the rules of React in ways that aren't always statically detectable, changing memoization can occasionally have unexpected results — for example, a previously memoized value used as a dependency for a useEffect somewhere in the component tree.
The fix is simple — the compiler gives you an escape hatch:
// If a component causes unexpected behavior with the compiler:
function ProblematicComponent() {
"use no memo"; // ← Opt this component out
// Component runs as before, without compiler optimization
}
The compiler is designed to skip code it can't safely optimize. If something does break, use "use no memo" to exclude that component and report the issue.
The Superpower Nobody's Talking About
Here's what makes React Compiler genuinely special — it can handle cases that manual memoization can't.
// Manual memoization breaks here — arrow function creates new reference
const handleClick = useCallback((item) => {
onClick(item);
}, [onClick]);
// Item still re-renders because () => handleClick(item) is new every time
<Item onClick={() => handleClick(item)} />
React Compiler is able to optimize this correctly with or without the arrow function, ensuring that Item only re-renders when props.onClick changes.
This is genuinely something you couldn't do correctly with manual memoization.
How to See What the Compiler Is Doing
React DevTools now shows compiler decisions on each component — a memoization badge appears next to components that are skip-memoized. You can hover over a prop in DevTools to see why it was considered "unchanged" or "changed."
Open React DevTools → Components tab → look for the memoization indicators on each component.
My Migration Strategy for Existing Projects
Don't delete everything at once. Here's what I'm doing:
Phase 1 — Enable compiler, don't delete anything yet
// Just add reactCompiler: true to your config
// Everything still works — compiler runs alongside existing memoization
Phase 2 — Check DevTools for over-memoization
- Look for components where the compiler and manual memo are both running
- These are your deletion candidates
Phase 3 — Delete in new code only
- Stop writing
useMemo/useCallbackin new components - Leave existing ones alone unless you're actively refactoring
Phase 4 — Gradually remove from existing code (with tests)
# Before deleting any useMemo/useCallback:
npm test
# Remove memoization
npm test
# If tests pass, ship it
For existing code, the recommendation is to either leave existing memoization in place (removing it can change compilation output) or carefully test before removing it.
The Bigger Picture
React Compiler isn't just a performance tool. It's a philosophy shift.
For years, we optimized React by thinking about renders — by manually deciding what should and shouldn't re-render. That model put cognitive load on developers, created bugs, and made code harder to read.
For years, React performance optimization has felt like a strange ritual. We sprinkle useMemo and useCallback everywhere, argue in code reviews about dependency arrays, and still end up with unnecessary re-renders. Now Meta has decided this is silly.
React Compiler says: just write what your component does. We'll handle when it renders.
That's a much better abstraction.
Quick Reference — Should I Delete It?
| Code | Delete in new code? | Delete in existing code? |
|---|---|---|
useMemo for calculations |
✅ Yes | ⚠️ Test first |
useCallback for handlers |
✅ Yes | ⚠️ Test first |
React.memo wrappers |
✅ Yes | ⚠️ Test first |
useCallback as effect dep |
❌ Keep | ❌ Keep |
useMemo for external libs |
❌ Keep | ❌ Keep |
Wrapping Up
The senior dev who left that comment on my PR was right.
Two years of carefully managing memoization — and now a compiler does it better than I ever did, in cases I didn't even know were possible.
Does this make useMemo and useCallback obsolete? Not completely. But for 80% of use cases? Yes. And that 80% is exactly where developers waste the most time.
Enable the compiler. Write simpler code. Let it do the work.
Are you using React Compiler in production yet? Or are you hesitant to remove your manual memoization? Drop your thoughts in the comments — I'd love to hear from teams who've already made the switch! 👇
Heads up: AI helped me write this.But the ideas, code review, and learning are all mine — AI just helped me communicate them better. I believe in being transparent about my process! 😊
Top comments (4)
Super useful article (I'd almost call it a "guide" or a "manual"), providing tips & tricks where other "React Compiler" articles only explain the obvious basics! The DevTools tip was a particularly nice one, and the overview tables ...
thank you so much That means a lot, especially coming from someone who clearly knows their stuff. I really tried to make it practical rather than just repeating the docs. Glad the DevTools tip and tables were helpful.
Well written, I like the clear distinction between 'useMemo', 'useCallback' and 'memo', how the React Compiler deals with those cases, and the cases it doesn't/can't deal with ...
Means a lot, thank you The limitations section was actually the part I spent most time on wanted to save people from future headaches lol. Have you tried the compiler yourself yet? Would love to hear your experience!