If you’ve spent any time in React performance discussions, you’ve seen the pattern: someone mentions slow renders, and someone else immediately suggests wrapping components in React.memo. Problem solved, right? Not even close. Most of the time, slapping React.memo on a component does nothing. Sometimes it makes things worse. Let’s walk through what this API actually does, when it genuinely improves performance, and when you’re just adding noise to your codebase.

What React.memo Actually Does
React.memo is a higher-order component. It takes your component and returns a new component that skips re-renders when its props haven’t changed. That’s the entire mechanism. React compares the previous props to the next props using shallow equality. If every prop is the same reference as before, React reuses the last rendered output. If any prop reference changed, React re-renders the component.
Notice I said “reference,” not “value.” This distinction is where most misuse originates. {'{{ count: 5 }}'} and {'{{ count: 5 }}'} are deeply equal but referentially different. Shallow comparison sees two different objects and triggers a re-render. The same applies to functions and arrays.
Here’s the basic syntax:
const MyComponent = React.memo(function MyComponent({ name, onClick }) {
return <div onClick={onClick}>{name}</div>
})
That’s it. No magic. No deep diffing of your entire component tree. Just a shallow prop check before deciding whether to render.
When React.memo Actually Helps
There are specific, identifiable situations where React.memo provides measurable benefit. Let’s be precise about them.
1. Heavy List Items Re-rendering from Parent State Changes
This is the textbook case. You have a parent component that holds state unrelated to most of its children, but every time that state updates, all children re-render. Consider a product list with a search filter in the parent:
function ProductList({ products }) {
const [searchTerm, setSearchTerm] = useState('')
const filtered = products.filter(p => p.name.includes(searchTerm))
return (
<>
<SearchBar value={searchTerm} onChange={setSearchTerm} />
{filtered.map(product => (
<ProductCard key={product.id} product={product} />
))}
</>
)
}
Every keystroke in SearchBar updates searchTerm, causing ProductList to re-render. Every ProductCard re-renders, even if its product prop hasn’t changed. If each card is expensiveâmaybe it calculates a discount, formats currency, or renders an imageâthose wasted renders add up. Wrapping ProductCard in React.memo prevents re-renders for cards whose product data stayed the same.
2. Preventing Cascading Re-renders in Component Trees
When a component deep in the tree receives a stable prop, React.memo acts as a firewall. Without it, a state change at the top can trigger renders all the way down. With it, you cut the cascade short. This matters when intermediate components pass callback props that they create inline, which would otherwise bust memoization in children.

3. Expensive Components with Stable Props
Some components do real work: SVG rendering, canvas drawing, complex layout calculations. If the props driving that work don’t change often, React.memo avoids redoing the expensive computation. The key phrase there is “don’t change often.” If the props change every render anyway, you’ve added comparison overhead for zero benefit.
When React.memo Hurts or Does Nothing
Here’s where most developers waste their time. These are the scenarios where React.memo is either useless or actively harmful.
Props That Change Every Render
If you pass an inline function, a new object, or a new array as a prop, shallow equality will fail every single time. Your memoized component re-renders just as often, but now React also spends time running the comparison. You’ve made things slightly slower and harder to read.
// This memo does nothing useful
const Parent = () => {
const [count, setCount] = useState(0)
return (
<MemoizedChild
items={[1, 2, 3]} // new array every render
onClick={() => {}} // new function every render
config={{ theme: 'dark' }} // new object every render
/>
)
}
I see this pattern constantly. Someone memoizes the child but doesn’t stabilize the props. The memo check runs, fails, and the child re-renders anyway. You’ve added overhead and complexity for nothing.
Components That Are Cheap to Render
Not every component needs memoization. A <span> with some text, a simple form input, a basic cardâthese render in microseconds. The shallow comparison itself might take as long as the render you’re trying to skip. Always measure before optimizing. Use React DevTools Profiler to identify actual bottlenecks, don’t guess.
Children Props and React.cloneElement
If your component receives children as a prop and the parent re-renders, the children reference changes. React.memo won’t help here unless you also memoize the JSX being passed as children, which is rarely worth the mental overhead.
Stabilizing Props: The Missing Half of the Equation
Using React.memo effectively means making sure the props you pass are referentially stable. You have tools for this.
- useMemo for objects and arrays that depend on specific values
- useCallback for functions passed as props
- Moving state down so the parent doesn’t re-render as often
const Parent = () => {
const [count, setCount] = useState(0)
const items = useMemo(() => [1, 2, 3], [])
const handleClick = useCallback(() => {
// handle click
}, [])
return <MemoizedChild items={items} onClick={handleClick} />
}
Now the memoization works. But notice what happened: you added three hooks and a wrapper just to skip a render. Is that render actually expensive enough to justify the complexity? If you can’t answer that question with profiler data, you’re optimizing blind.

Custom Comparison Functions
Sometimes shallow equality isn’t enough. React.memo accepts a second argument: a custom comparison function.
const MemoChild = React.memo(Child, (prevProps, nextProps) => {
return prevProps.id === nextProps.id && prevProps.status === nextProps.status
})
Return true to skip the re-render (props are equal), false to allow it. This looks handy, but tread carefully. A custom comparison function runs every render. If it’s doing deep equality on large objects, you might spend more time comparing than rendering. Keep custom comparisons narrow and cheap.
There’s also a subtle trap here: the comparison function uses the opposite return convention from what most developers expect. Returning true means “props are the same, skip the render.” Returning false means “props differ, render.” I’ve seen bugs from developers getting this backwards. The React docs cover this explicitlyâread them carefully if you go this route.
Common Mistakes I See Repeatedly
Memoizing Everything by Default
Some teams wrap every component in React.memo as a convention. This is a performance anti-pattern. You’re adding comparison overhead to every component, including the ones that re-render on every prop change anyway. Memoization is a targeted solution, not a blanket policy.
Forgetting Context Consumers
If a component consumes React Context, it re-renders when the context value changes, regardless of React.memo. Memoizing the component won’t prevent that re-render. If you want to prevent context-driven re-renders, you need to either split your context or use a selector pattern. The React team’s RFCs have discussed built-in context selectors, but for now, check out libraries like use-context-selector if this is a real bottleneck.
Measuring Wrong
Console logging inside a component body to check if it re-renders tells you that it rendered, not how long it took. A component re-rendering isn’t inherently a problem. A component re-rendering and taking 50ms to produce outputâthat’s a problem. Use the Profiler. Look at committed render times. Focus on components that actually appear in the flame chart as bottlenecks.
A Practical Decision Framework
Instead of memorizing rules, work through these questions when considering React.memo:
- Is the component expensive to render? Profile it. If it commits in under a millisecond, stop here. Don’t memoize.
- Does it re-render frequently with the same props? Check with React DevTools. If props change every parent render, memo won’t help unless you stabilize them.
- Can you stabilize the props cheaply? If stabilizing props requires wrapping everything in
useMemoanduseCallback, consider whether moving state down or restructuring the component tree would be simpler. - Is the memoization measurable? Add
React.memo, profile again, and check the difference. If you can’t measure the improvement, remove the memo.
FAQ
Does React.memo do deep comparison of props?
No. By default, React.memo uses shallow equality comparison. It checks if each prop is the same reference as before, not whether objects or arrays have the same contents. This is why passing inline objects or arrays defeats memoization. You can provide a custom comparison function as a second argument, but that comes with its own performance cost.
Is React.memo the same as shouldComponentUpdate?
Conceptually similar, but React.memo is for function components and shouldComponentUpdate was for class components. They both let you control whether a component re-renders based on prop changes. The key difference is that React.memo uses shallow comparison by default, while shouldComponentUpdate required you to write the comparison logic yourself every time.
Should I wrap every component in React.memo as a best practice?
Absolutely not. Memoization has a cost: the shallow comparison runs on every render, and wrapping components adds cognitive overhead for anyone reading the code. Apply React.memo when you have profiler evidence that a specific component is a bottleneck, not as a default. Treating it as a blanket optimization is one of the most common mistakes I see in React codebases.
The Bottom Line
React.memo is a scalpel, not a sledgehammer. It works when you have expensive components receiving stable props while their parent re-renders for unrelated reasons. It fails when you apply it without stabilizing props, when you use it on cheap components, or when you treat it as a default optimization. Profile first. Identify real bottlenecks. Stabilize props. Then memoize. Skip the ceremony, measure the result, and remove anything that doesn’t produce a measurable improvement.