useMemo vs React.memo
Short answer
useMemo is a hook that caches a computed value inside a component so it is not recalculated on every render. React.memo is a wrapper around a whole component that skips its re-render when its props are shallowly equal. One memoises a value, the other memoises a render — and they are usually needed together.
| useMemo | React.memo | |
|---|---|---|
| What it is | A hook | A higher-order component |
| What it caches | The result of a calculation | A component's rendered output |
| Where you write it | Inside the component body | Around the component, at export |
| Recomputes when | A dependency changes | A prop is not shallowly equal |
| Prevents a re-render | No | Yes |
| Prevents a recalculation | Yes | No |
| Comparison used | Object.is on each dependency | Object.is on each prop |
| Typical reason to reach for it | An expensive calculation, or a stable object prop | A heavy child that re-renders for no reason |
The two things being memoised
Both exist because React re-runs a component function on every render, and that function does two kinds of work you might not want repeated: it computes values, and it produces a subtree that React then has to reconcile. useMemo addresses the first, React.memo the second. They sit at different levels, which is why comparing them as alternatives leads people astray — you can need both in the same file, for the same interaction.
1const Chart = React.memo(function Chart({ points }) {
2 return <svg>…</svg>;
3 // → this whole function is skipped when `points` is
4 // the same reference as last render
5});
6
7function Dashboard({ rows }) {
8 const points = useMemo(
9 () => rows.map(toPoint).filter(Boolean), // expensive
10 [rows]
11 );
12 // → recomputed only when `rows` changes; the same array
13 // reference is handed to Chart on every other render
14
15 return <Chart points={points} />;
16}Remove the useMemo and React.memo stops working, because rows.map returns a brand-new array every render and a new array is never shallowly equal to the old one. Remove the React.memo and useMemo still saves the calculation, but Chart re-renders anyway. That mutual dependence is the single most useful thing to understand about the pair.
React.memo: skipping a subtree
By default, when a component re-renders, React re-renders all of its children — not because their props changed, but because React does not know whether they did without asking. React.memo is you telling React it is worth asking: before re-rendering this component, compare each of its props to last time, and if they are all the same, reuse the previous output and skip the whole subtree.
1function Parent() {
2 const [count, setCount] = useState(0);
3 return (
4 <>
5 <button onClick={() => setCount(c => c + 1)}>{count}</button>
6 <Heavy title="Report" />
7 </>
8 );
9}
10
11function Heavy({ title }) {
12 console.log("Heavy rendered");
13 return <h2>{title}</h2>;
14}
15// → "Heavy rendered" on every click, even though title never changes
16
17const Heavy = React.memo(function Heavy({ title }) {
18 console.log("Heavy rendered");
19 return <h2>{title}</h2>;
20});
21// → "Heavy rendered" once. Clicks re-render Parent only.The saving scales with the size of the subtree, not the size of the component. Memoising a leaf that renders one span saves almost nothing. Memoising the root of a list with two hundred rows saves two hundred renders plus the reconciliation of everything they produce, which is why memo belongs at boundaries rather than sprinkled everywhere.
useMemo: caching a value
useMemo runs the function you give it, stores the result, and returns that same result on subsequent renders until one of the dependencies changes. It has two distinct jobs, and mixing them up is the source of most bad memo code: skipping expensive work, and keeping a reference stable so something downstream can rely on it.
1// Job 1 — the calculation is genuinely expensive
2const sorted = useMemo(
3 () => hugeList.slice().sort(byPrice),
4 [hugeList]
5);
6// → 5,000-item sort runs once per change of hugeList,
7// not on every keystroke elsewhere in the component
8
9// Job 2 — the calculation is trivial, the REFERENCE is the point
10const style = useMemo(() => ({ width, height }), [width, height]);
11// → without this, { width, height } is a new object every render,
12// so <Canvas style={style} /> wrapped in React.memo never bails out
13
14// Not worth memoising — cheaper than the memo bookkeeping
15const total = useMemo(() => a + b, [a, b]); // ← just write a + bThis is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399Why React.memo so often does nothing
This is the part that costs people real time. React.memo runs a shallow comparison, which means Object.is on each prop. Primitives compare by value and behave intuitively. Objects, arrays and functions compare by identity — and JSX creates fresh ones on every render, so they are never equal to the previous render's version, no matter how identical their contents look.
1const Row = React.memo(RowImpl);
2
3<Row user={user} onSelect={() => pick(user.id)} />
4// → new arrow function every render — memo never bails out
5
6<Row user={user} tags={[]} />
7// → new array literal every render — same problem
8
9<Row user={{ ...user }} />
10// → new object every render
11
12<Row user={user}>{<Badge />}</Row>
13// → children is a new element object every renderThe fix is to make each of those references stable at the source: useCallback for the handler, useMemo for the object or array, and a module-level constant for anything that never changes at all. Hoisting a value like an empty array out of the component is free and beats memoising it.
1const NO_TAGS = []; // module scope: one array for the app's lifetime
2
3function List({ users, onPick }) {
4 const handleSelect = useCallback(
5 (id) => onPick(id),
6 [onPick]
7 );
8
9 return users.map(u => (
10 <Row key={u.id} user={u} tags={NO_TAGS} onSelect={handleSelect} />
11 ));
12 // → Row now bails out for every user whose object did not change
13}Where useCallback fits
useCallback is not a third mechanism — it is useMemo specialised for functions. useCallback(fn, deps) is exactly useMemo(() => fn, deps). It exists because passing a stable function reference is such a common need that a dedicated hook reads better than a memo returning a lambda.
1const a = useCallback((id) => save(id), [save]);
2const b = useMemo(() => (id) => save(id), [save]);
3// → a and b behave identically; both stay the same reference
4// until `save` changes
5
6// The difference is only what you get back:
7useMemo(() => compute(x), [x]); // → the RESULT of calling it
8useCallback(() => compute(x), [x]); // → the FUNCTION itself, uncalledSo the practical trio is: React.memo to skip a component, useMemo to keep a value or object stable, and useCallback to keep a handler stable. The last two exist mostly in service of the first, or of a hook dependency array — which is a useful way to remember when they earn their place.
The cost side of the trade
None of these is free. React.memo adds a shallow prop comparison before every potential render and keeps the previous element tree alive. useMemo and useCallback each store a value and a dependency array for as long as the component is mounted. When the memoised work is cheap, you have added allocation, comparison and cognitive load in exchange for nothing measurable.
| Situation | Reach for | Why |
|---|---|---|
| Child renders a large subtree with stable props | React.memo | Skips the subtree and its reconciliation |
| Sort or filter over thousands of items | useMemo | The calculation dominates the render |
| Object or array passed to a memoised child | useMemo | Keeps the reference stable so memo can bail |
| Callback passed to a memoised child | useCallback | Same reason, for functions |
| Value used in another hook's dependency array | useMemo / useCallback | Stops the effect from firing every render |
| A sum, a template string, a boolean flag | Nothing | The memo costs more than the work |
| A small component that renders a few nodes | Nothing | Rendering it is cheaper than comparing props |
React 19, the Compiler, and what may become unnecessary
React Compiler analyses your components at build time and inserts memoisation automatically, which removes most of the reason to write useMemo, useCallback and React.memo by hand. In a project where it is enabled and working, hand-written memoisation is mostly noise. It is not a reason to stop understanding the mechanism — the compiler memoises for exactly the reasons described above, and when it bails out on a component, you need to know what it was trying to do.
React 19 also renamed nothing here: React.memo, useMemo and useCallback all still exist and behave as before. What changed around them is that Server Components remove a whole class of client re-renders entirely, which is usually a bigger win than any memoisation you could add.
Can I wrap a component in both React.memo and useMemo?
You would not memoise the same thing twice, but a component wrapped in React.memo commonly contains useMemo calls inside it. What you should not do is useMemo(() => <Child />, deps) as a substitute for React.memo — it memoises the element rather than the component, which works but hides the intent and does not compose.
Does React.memo do a deep comparison?
No, shallow only — Object.is on each prop. You can pass a second argument, a custom areEqual(prev, next) function, to compare differently. Be careful: a deep comparison on a large object can cost more than the render you are skipping, and returning true incorrectly produces a component that never updates.
Does memo stop a re-render caused by state or context?
No. React.memo only guards against re-renders caused by a parent rendering. If the component has its own state, or consumes a context whose value changed, it re-renders regardless of props. That is the second most common reason memo appears not to work.
Is useMemo guaranteed to cache?
No. React documents useMemo as a performance hint, and it may discard cached values — for example to free memory for offscreen content. Never use it for anything whose absence would break correctness; if you need something to run exactly once, that is useEffect or a ref, not useMemo.
What is the difference between React.memo and PureComponent?
They are the same idea in the two component models. PureComponent implements shouldComponentUpdate with a shallow prop and state comparison for classes; React.memo does the shallow prop comparison for function components. Neither compares deeply, and both are defeated by fresh object references.
Frequently asked questions
- What is the difference between useMemo and React.memo?
- useMemo is a hook used inside a component to cache a calculated value between renders. React.memo is a wrapper placed around a component that skips its re-render when its props are shallowly equal. One memoises a value, the other memoises a render, and they are frequently used together.
- Does React.memo cache values?
- No. It caches the component's rendered output, keyed on its props. Any values calculated inside the component are recalculated whenever the component does render — caching those is what useMemo is for.
- Why is React.memo not working?
- Almost always because a prop is a new reference on every render: an inline arrow function, an object or array literal, or JSX passed as children. Shallow comparison sees those as changed. Stabilise them with useCallback, useMemo, or a module-level constant.
- When should I use React.memo?
- When a component renders a genuinely large subtree, its props are stable, and profiling shows it re-rendering for no reason — typically list rows, charts, editors and heavy panels. Wrapping small components is usually a net loss, because comparing props costs more than rendering them.
- When should I use useMemo?
- For a calculation heavy enough to show up in a profile, or to keep an object, array or derived value referentially stable for a memoised child, a hook dependency array, or a context value. For simple arithmetic and string building, skip it.
- What is the difference between useMemo and useCallback?
- useMemo returns the result of calling the function you pass; useCallback returns the function itself without calling it. useCallback(fn, deps) is defined as useMemo(() => fn, deps). Use useMemo for values, useCallback for handlers you pass down.
- Does React.memo prevent re-renders from state changes?
- No. It only blocks re-renders that come from a parent re-rendering. A component's own useState update, or a context value it consumes changing, re-renders it regardless of how its props compare.
- Is it bad to use useMemo everywhere?
- Yes, in the ordinary sense that it adds cost and clutter without benefit. Every memo allocates a dependency array and compares it on each render, holds a value in memory, and makes the code harder to read. Reserve it for measured wins and for reference stability.
- Can React.memo take a custom comparison function?
- Yes — a second argument areEqual(prevProps, nextProps) returning true to skip the render. Note the inverted sense compared with shouldComponentUpdate. Use it sparingly: a deep comparison can cost more than the render, and a wrong true produces a component that never updates.
- Does React.memo work with children?
- It compares children like any other prop, and JSX children are a new element object on every parent render, so memo usually fails to bail out. Passing children through a memoised wrapper only helps if the parent itself memoises that element.
- Does React Compiler make useMemo and React.memo obsolete?
- It removes most of the need to write them by hand, because it inserts equivalent memoisation at build time. Understanding the mechanism still matters: you need it to read compiled behaviour, to debug a component the compiler bails out on, and to work in the many codebases that do not have it enabled.
- Is React.memo the same as PureComponent?
- It is the function-component equivalent. PureComponent gives a class a shallow comparison of props and state; React.memo gives a function component a shallow comparison of props. Both are defeated by newly created object, array and function props.
This is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399