RIP useMemo. You served us well, but your watch is ended.
For years, we've polluted our codebases with dependency arrays, manual caching, and the endless "is this expensive enough to memoize?" debate.
React 19 and the React Compiler have changed the game. It's time to delete those hooks.
The Shift is Real: You open a file. You see useMemo wrapping a simple object. You see useCallback passing a function to a child. You sigh. This is the "React Tax" we've paid for performance. But in 2026, this tax has been abolished.
The React Compiler (formerly React Forget) isn't just a tool; it's a paradigm shift. It automatically memoizes your components, props, and hooks logic at build time. This means you get the performance of a perfectly tuned app with the simplicity of a "naive" render implementation. In this deep dive, we'll explore how this magic works, look at the stark difference in code cleanliness, and identify the few edge cases where you still need to be the pilot.
02. What is the React Compiler?
Historically, React re-renders were "reactive" but often excessive. If a parent re-rendered, all children re-rendered unless you explicitly told them not to (via React.memo, useMemo, etc.). The React Compiler inverts this model.
It is a build-time tool (like Babel or SWC plugins) that deeply understands JavaScript and React's rules. It analyzes your data flow and automatically inserts memoization logic where it detects it's necessary. It's "fine-grained reactivity" without the manual overhead.
The Promise
- No more dependency arrays: You don't list dependencies; the compiler infers them.
- Default performance: Apps are fast by default, not by optimization.
- Cleaner code: Business logic stands out; performance boilerplate disappears.
03. Before vs. After: A Code Comparison
🚫 Before: Hook Hell
function ExpensiveComponent({ data, onSelect }) {
// Manual Calculation
const processed = useMemo(() => {
return data.map(item => expensiveFn(item));
}, [data]);
// Stable Reference
const handleClick = useCallback((id) => {
onSelect(id);
logAnalytics('select', id);
}, [onSelect]);
const options = useMemo(() => ({
theme: 'dark'
}), []);
return (
<List
items={processed}
onClick={handleClick}
config={options}
/>
);
}
Look at the noise. Dependency arrays, wrapping functions, stable object references... easy to get wrong, hard to read.
✅ After: Pure Logic
function ExpensiveComponent({ data, onSelect }) {
// Just write JavaScript!
const processed = data.map(item => expensiveFn(item));
function handleClick(id) {
onSelect(id);
logAnalytics('select', id);
}
const options = { theme: 'dark' };
return (
<List
items={processed}
onClick={handleClick}
config={options}
/>
);
}
The compiler sees processed depends on data. It sees options is static. It handles the caching automagically.
04. Under the Hood: Auto-Memoization
The compiled code doesn't just wrap everything in useMemo. It uses a lower-level primitive, often referred to conceptually as useMemoCache.
The compiler generates code that checks if inputs have changed using strict equality ===. If they haven't, it returns a cached value. This is similar to how you manually wrote memoization, but it's applied granually to groups of statements.
// Conceptual Output of the Compiler
function CompiledComponent(props) {
const $ = useMemoCache(10); // Hook to hold cached values
const { data } = props;
let processed;
// If data hasn't changed...
if ($[0] !== data) {
processed = data.map(item => expensiveFn(item));
$[0] = data;
$[1] = processed;
} else {
processed = $[1];
}
// ... and so on
}
This "Memoization Cache" hook is highly optimized and allows React to skip re-executing blocks of code within a component, not just the whole component itself.
05. When the Compiler Fails (and How to Fix It)
Reality Check: The compiler is good, but it's not omniscient. It relies on the "Rules of React". If you break them, it bails out.
1. Mutation of Props or State
If you mutate variables that are tracked by React (like doing props.user.name = 'Bob'), the compiler cannot safely optimize. You must treat data as immutable.
2. Dynamic Dependency Arrays (Legacy)
If you have existing code doing weird things with useEffect dependencies or suppression comments, the compiler might skip optimizing that component.
3. "use no memo"
There's an escape hatch directive ("use no memo") for highly dynamic components where the overhead of checking the cache outweighs the benefit of caching (rare, but possible).
06. Educational Value: The New Mental Model
As an educator or senior dev, how do you explain this?
- Beginners: Don't teach
useMemo/useCallbackearly anymore. Teach JavaScript. React just "works". - Intermediates: Focus on referential identity when crossing boundaries (e.g. passing things to external libraries or Context).
- Experts: Your job is now Architecture, not Micro-optimization. Focus on component boundaries, data fetching strategies (RSC), and UX transitions.