React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

memoizationperformanceoptimizationhooks

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 fibonacci calculation is memoized and only recalculates when num changes
  • 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
  • useMemo is a performance optimization, not a semantic guarantee — React may discard the cache in some cases (e.g., offscreen components)

Parameters & Return Values

ParameterTypeDescription
factory() => TFunction that computes the value to memoize
dependenciesunknown[]Array of reactive values the computation depends on
ReturnTypeDescription
memoizedValueTCached 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 useMemo adds 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, useMemo recalculates every time anyway. Fix: Memoize the dependency itself, or restructure to depend on primitives.

  • Side effects in the factoryuseMemo runs during rendering; side effects (fetching, subscriptions) break React's rules. Fix: Move side effects to useEffect or event handlers.

  • Not a guarantee — React may drop memoized values for offscreen components and recalculate later. Fix: Never rely on useMemo for correctness — only for performance.

Alternatives

AlternativeUse WhenDon't Use When
No memoizationThe computation is cheap (string concatenation, simple math)Computation is measurably slow
React.memoYou want to skip re-rendering an entire child componentYou need to memoize a value, not a component
useCallbackYou need a stable function reference, not a stable valueYou need to cache a non-function value
useDeferredValueYou want to defer rendering expensive content without blocking inputYou need to cache computation results
Server-side computationExpensive work can happen at build time or request timeData 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 useMemo when 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 a React.memo child.
  • 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.
  • useMemo is 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 to useMemo(() => fn, deps)).
  • Use useMemo for values, useCallback for functions.
Can I run side effects inside useMemo?
  • No. useMemo runs during rendering, so side effects (fetching, subscriptions, DOM mutations) break React's rules.
  • Move side effects to useEffect or 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 change
How 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: Config
Gotcha: Does wrapping every value in useMemo improve performance?
  • No. useMemo itself 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 data hasn't changed.
  • Prefer React.memo on 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.
  • useMemo is 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]) infers number.
  • Explicit generics are only needed when inference is too wide or you want a narrower type.
  • useCallbackuseMemo for functions
  • useEffect — stabilize dependencies with useMemo to avoid re-running effects
  • useDeferredValue — defer rendering of expensive content
  • useRef — store values without triggering re-renders