Premature memoization means wrapping components and values in memo, useMemo, or useCallback before you have measured a real slowdown. The extra comparison work can cost more than the work it is supposed to save. It also makes the component harder to read, because every cached value now carries a dependency array to keep in sync.
Memoization is not free
memo compares props with Object.is on every render. useMemo and useCallback compare their dependency arrays the same way. That check is cheap per item but never zero, and it runs even when the component would have rendered quickly anyway.
import { memo } from "react";
const Row = memo(function Row({ label }) {
return <li>{label}</li>;
});A Row that returns a single list item is already fast. Adding memo means React compares label on every render before deciding whether to render. For a dozen items that bookkeeping is invisible, but across hundreds of memoized leaves it adds up while buying nothing.
Cached values also stay in memory until React discards them, so wrapping a large list or tree keeps that extra copy alive between renders.
It can be slower, not faster
When a memoized component receives a new prop on every render, the comparison always fails and the component renders anyway. The wrapper adds a full prop comparison on top of the render, so the total cost is slightly higher than no memo at all.
Imagine a component that renders in under a millisecond. memo still walks every prop with Object.is, so the comparison and the render cost about the same, and you have paid twice for no savings.
The same applies to useMemo and useCallback. A value or function whose dependencies change every render is recomputed anyway, and the Hook only adds the dependency comparison on top.
Custom comparisons can be much worse
A custom comparison function can easily cost more than the render it is trying to skip.
import { memo } from "react";
const Chart = memo(function Chart({ dataPoints }) {
return <div>chart</div>;
}, arePropsEqual);
function arePropsEqual(oldProps, newProps) {
return (
JSON.stringify(oldProps.dataPoints) ===
JSON.stringify(newProps.dataPoints)
);
}Serializing large arrays to compare them can take longer than re-rendering the chart itself. The official docs recommend measuring a custom comparison before trusting it, because deep equality checks can freeze the app if the data structure grows.
It also costs readability
Every memo, useMemo, and useCallback adds a dependency array that must stay correct as the code changes. A stale array either breaks memoization silently or, in a custom comparison, closes over old values. Teams that memoize by habit spend review time checking arrays that never should have existed.
It hides the real problem
Premature memoization often patches a structural issue. Lifting state too high, storing derived values, or running work in an Effect causes re-renders that memo only hides. Fixing the source removes the re-render entirely and keeps the component tree simple.
A profile of the same app before and after the structural fix usually shows the difference, while memo alone leaves the underlying re-renders in place.
When memoization earns its place
- Measure with the Profiler and confirm a re-render is the bottleneck.
- Wrap only the component that renders often with unchanged props.
- Keep the props stable so the comparison can succeed.
The full measuring workflow is in how to find unnecessary re-renders with React Profiler, and the memo mechanics are in React memo explained. Applied only after a measurement, memoization stops being premature and starts paying for itself. Measure, wrap, then remove the wrapper if the commit duration does not improve.
The compiler removes the tradeoff
React Compiler applies memoization automatically at build time, so codebases that enable it can usually skip the manual wrappers and their comparison overhead altogether. When it is on, the compiler handles memoization without the guessing, and manual memo becomes mostly an escape hatch for places that need precise control.
Rune AI
Key Insights
- Memoization is not free: it compares props and dependencies.
- memo buys nothing when props change on every render.
- Custom deep comparisons can cost more than rendering.
- Measure with the Profiler before wrapping anything.
- React Compiler applies memoization automatically.
Frequently Asked Questions
Does memo make a cheap component faster?
When should I add memoization?
Conclusion
Memoization is a tool with a cost. Comparing props and dependencies takes time, and when the work is cheap or the props always change, the wrapper only adds overhead. Measure first, then memoize the specific component the Profiler proves is slow.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.