useMemo Hook
Cache the result of an expensive computation between renders.
Recipe
Quick-reference recipe card — copy-paste ready.
const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]);
// Memoize a derived array/object to stabilize references
const filtered = useMemo(
() => items.filter((item) => item.active),
[items]
);When to reach for this: You have an expensive calculation that should not re-run on every render, or you need a stable object/array reference for a dependency array or child prop.
Working Example
"use client";
import { useMemo, useState } from "react";
function fibonacci(n: number): number {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
export function FibonacciCalculator() {
const [num, setNum] = useState(10);
const [theme, setTheme] = useState<"light" | "dark">("light");
const result = useMemo(() => fibonacci(num), [num]);
return (
<div className={theme === "dark" ? "bg-gray-900 text-white p-4" : "bg-white text-black p-4"}>
<label className="flex items-center gap-2">
n =
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
className="w-20 border rounded px-2 py-1"
max={40}
/>
</label>
<p className="mt-2 font-mono">fibonacci({num}) = {result}</p>
<button
onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))}
className="mt-2 px-3 py-1 border rounded text-sm"
>
Toggle theme
</button>
</div>
);
}What this demonstrates:
- Expensive
fibonaccicalculation is memoized and only recalculates whennumchanges - Toggling the theme does not recompute the fibonacci result
- Without
useMemo, every re-render (including theme toggle) would re-run the expensive calculation
Deep Dive
How It Works
- On the first render, React calls your factory function and stores the result
- On subsequent renders, React compares each dependency with its previous value using
Object.is - If all dependencies are the same, React returns the cached result without calling the factory
- If any dependency changed, React calls the factory again and caches the new result
useMemois a performance optimization, not a semantic guarantee — React may discard the cache in some cases (e.g., offscreen components)
Parameters & Return Values
| Parameter | Type | Description |
|---|---|---|
factory | () => T | Function that computes the value to memoize |
dependencies | unknown[] | Array of reactive values the computation depends on |
| Return | Type | Description |
|---|---|---|
memoizedValue | T | Cached result of the factory function |
Variations
Stabilize an object for a dependency array:
const options = useMemo(
() => ({ page, pageSize, sortBy }),
[page, pageSize, sortBy]
);
useEffect(() => {
fetchData(options);
}, [options]);Memoize a filtered/sorted list:
const sortedUsers = useMemo(
() => [...users].sort((a, b) => a.name.localeCompare(b.name)),
[users]
);Memoize a component tree (rare):
const chart = useMemo(
() => <ExpensiveChart data={data} />,
[data]
);TypeScript Notes
// Return type is inferred from the factory
const total = useMemo(() => items.reduce((sum, i) => sum + i.price, 0), [items]);
// total: number
// Explicit generic when inference is too wide
const config = useMemo<Config>(() => ({ retries: 3, timeout: 5000 }), []);Gotchas
-
Premature optimization — Wrapping every value in
useMemoadds complexity without measurable benefit for cheap computations. Fix: Profile first. Only memoize when you can measure a performance problem. -
Missing dependencies — Omitting a dependency causes the memoized value to be stale. Fix: Include all reactive values used inside the factory.
-
Unstable dependency references — If a dependency is a new object/array on every render,
useMemorecalculates every time anyway. Fix: Memoize the dependency itself, or restructure to depend on primitives. -
Side effects in the factory —
useMemoruns during rendering; side effects (fetching, subscriptions) break React's rules. Fix: Move side effects touseEffector event handlers. -
Not a guarantee — React may drop memoized values for offscreen components and recalculate later. Fix: Never rely on
useMemofor correctness — only for performance.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| No memoization | The computation is cheap (string concatenation, simple math) | Computation is measurably slow |
React.memo | You want to skip re-rendering an entire child component | You need to memoize a value, not a component |
useCallback | You need a stable function reference, not a stable value | You need to cache a non-function value |
useDeferredValue | You want to defer rendering expensive content without blocking input | You need to cache computation results |
| Server-side computation | Expensive work can happen at build time or request time | Data depends on client-side state |
When is it worth it? As a rule of thumb, memoize when the computation takes more than ~1ms or when the result is used as a dependency for useEffect, useCallback, or passed to a React.memo child.
FAQs
When should I actually use useMemo vs just computing the value inline?
- Use
useMemowhen the computation takes more than ~1ms (profile first). - Also use it when the result is used as a dependency for
useEffect,useCallback, or passed to aReact.memochild. - For cheap operations like string concatenation or simple math, skip
useMemo.
Is useMemo a guarantee that my function will never re-run with the same deps?
- No. React may discard memoized values for offscreen components and recalculate later.
useMemois a performance optimization, not a semantic guarantee.- Never rely on it for correctness -- only for performance.
Gotcha: Why does my useMemo recalculate on every render even though I provided dependencies?
- One of your dependencies is likely an unstable reference (a new object or array created during render).
- React compares dependencies with
Object.is, so a new reference means "changed." - Fix by memoizing the dependency itself or restructuring to depend on primitives.
What is the difference between useMemo and useCallback?
useMemo(() => value, deps)caches the return value of the factory function.useCallback(fn, deps)caches the function itself (equivalent touseMemo(() => fn, deps)).- Use
useMemofor values,useCallbackfor functions.
Can I run side effects inside useMemo?
- No.
useMemoruns during rendering, so side effects (fetching, subscriptions, DOM mutations) break React's rules. - Move side effects to
useEffector event handlers. - The factory function must be pure.
How do I stabilize an object passed to useEffect dependencies using useMemo?
const options = useMemo(
() => ({ page, pageSize, sortBy }),
[page, pageSize, sortBy]
);
useEffect(() => {
fetchData(options);
}, [options]); // stable reference, effect only re-runs when values changeHow do I type useMemo with an explicit generic in TypeScript?
interface Config {
retries: number;
timeout: number;
}
const config = useMemo<Config>(
() => ({ retries: 3, timeout: 5000 }),
[]
);
// config: ConfigGotcha: Does wrapping every value in useMemo improve performance?
- No.
useMemoitself has overhead -- storing the value, comparing dependencies, and maintaining the cache. - For cheap computations, the memoization overhead can exceed the computation cost.
- Profile first and only memoize where you measure a real performance benefit.
Can I use useMemo to memoize a React component tree?
const chart = useMemo(
() => <ExpensiveChart data={data} />,
[data]
);- This is valid but rare. It prevents React from re-rendering the subtree when
datahasn't changed. - Prefer
React.memoon the component itself for most cases.
How does useMemo compare to computing derived values in a server component?
- Server components can compute expensive values at request time or build time with zero client-side cost.
useMemois for client-side computation that depends on client state.- If the data doesn't depend on client interactions, prefer server-side computation.
What happens to the TypeScript return type if I don't provide an explicit generic?
- TypeScript infers the return type from the factory function automatically.
useMemo(() => items.reduce((sum, i) => sum + i.price, 0), [items])infersnumber.- Explicit generics are only needed when inference is too wide or you want a narrower type.
Related
- useCallback —
useMemofor functions - useEffect — stabilize dependencies with
useMemoto avoid re-running effects - useDeferredValue — defer rendering of expensive content
- useRef — store values without triggering re-renders