BitlystBitlyst
PostsSeriesTopicsAbout

Bitlyst

Short, diagram-first posts on JavaScript, React & Next.js. Follow via RSS or the newsletter.

© 2026 Mohsen Fallahnejad

PostsSeriesTopicsLinksAboutRSSGitHubX
Posts/react
Intermediate3 min read

React memo vs useMemo vs useCallback — When to Use Which

Three React APIs that all say 'memoize' — but they do different things. Clear explanations, examples, and a cheatsheet.

Mohsen Fallahnejad

Mohsen Fallahnejad

Jun 28, 2026Discuss

React memo vs useMemo vs useCallback

All three are about skipping unnecessary work. But they work differently.


🧠 The Core Idea#

React re-renders a component whenever its parent re-renders — even if nothing changed. Memoization lets you say:

"Only redo this if the inputs actually changed."


1️⃣ React.memo — Skip re-rendering a component#

Wraps a component. Skips re-render if props didn't change.

tsx
const Button = React.memo(({ onClick, label }: { onClick: () => void; label: string }) => {
  console.log("Button rendered");
  return <button onClick={onClick}>{label}</button>;
});

Without memo: Button re-renders every time the parent does.
With memo: Button skips re-render if onClick and label are the same.


2️⃣ useMemo — Cache a computed value#

Memoizes the result of an expensive calculation.

tsx
const total = useMemo(() => {
  return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
  • Runs only when items changes.
  • Returns the cached value on other renders.

❌ Without useMemo: recalculates on every render.
✅ With useMemo: recalculates only when items changes.


3️⃣ useCallback — Cache a function#

Memoizes a function reference so it stays stable across renders.

tsx
const handleClick = useCallback(() => {
  console.log("clicked", id);
}, [id]);

Without useCallback, a new function is created every render — which breaks React.memo on child components.


🔗 How They Work Together#

React.memo compares props with ===. Functions and objects always fail === unless their reference is stable.

tsx
// ❌ Button re-renders every time — new function each render
<Button onClick={() => doSomething(id)} label="Click" />

// ✅ Button skips re-render — stable reference
const handleClick = useCallback(() => doSomething(id), [id]);
<Button onClick={handleClick} label="Click" />
  1. 1

    Parent re-renders

  2. 2

    Props compared (===)

then it depends
Same reference
  1. ↳memo skips re-render
New reference
  1. ↳memo re-renders child

⚠️ When NOT to Use Them#

Memoization has a cost — it takes memory and adds complexity. Don't use it by default.

SituationUse memoization?
Cheap calculation❌ Not worth it
Component renders rarely❌ Skip it
Heavy computation (sort, filter on large arrays)✅ Yes
Passing callbacks to memoized children✅ Yes
Passing objects/arrays as props to memoized children✅ Yes

🧪 Quick Example: All Three Together#

tsx
const ProductList = React.memo(({ items, onSelect }: Props) => {
  // useMemo: only recalculate when items change
  const total = useMemo(
    () => items.reduce((s, i) => s + i.price, 0),
    [items]
  );

  return (
    <div>
      <p>Total: ${total}</p>
      {items.map((item) => (
        <ProductCard key={item.id} item={item} onSelect={onSelect} />
      ))}
    </div>
  );
});

function App() {
  const [cart, setCart] = useState([]);

  // useCallback: stable reference so ProductList doesn't re-render
  const handleSelect = useCallback(
    (item) => setCart((prev) => [...prev, item]),
    []
  );

  return <ProductList items={products} onSelect={handleSelect} />;
}

✅ Summary#

APIWrapsSkips
React.memoComponentRe-render when props unchanged
useMemoComputed valueRecalculation when deps unchanged
useCallbackFunctionRecreation when deps unchanged

✨ Final Takeaway#

React.memo stops a component from re-rendering. useMemo stops a value from recalculating. useCallback stops a function from being recreated. Use them together — memo needs useCallback to actually work.

Filed under#react#performance#hooks#memoization#frontend

Was this worth your time?

One tap tells me what to write more of.

Mohsen Fallahnejad

Written by

Mohsen Fallahnejad

Frontend engineer explaining JavaScript, React & Next.js internals in posts you can finish over coffee. More about me

X ↗GitHub ↗LinkedIn ↗
← OlderCDN Explained: What It Is, How It Works, and How to Use It in Next.jsNewer →JavaScript Generators & Iterators — Lazy Sequences Explained

Start the discussion

Questions, corrections, a better approach? I read and reply to every comment.

Read next

React useEffect Dependencies — The Bug That Keeps Coming Back

Why your effect reads old state, fires twice, or loops forever — and the simple rules to fix it (interview-ready).

Jul 8, 20266 min read1

useDeferredValue Explained: Keeping React UIs Responsive

How useDeferredValue works under the hood, when to use it, and how it differs from useTransition.

Dec 12, 20251 min read

useTransition Explained: Make React Feel Instant

How useTransition works under the hood, when to use it, and how it differs from useDeferredValue.

Dec 12, 20253 min read

Get new posts in your inbox

No spam. Unsubscribe any time.

3 min left
Discuss👍 React
👍