In short
React re-renders a lot, and usually that's fine. When a slow calculation or a heavy list starts lagging, memoization lets React skip work it has already done.
Caching the result of an expensive computation or a component's render output (`useMemo`, `useCallback`, `React.memo`) so it isn't recalculated unnecessarily.

Think of it like this
Tables you learnt by heart
Nobody works out 7 × 8 from scratch in the exam hall. You memorised 56 years ago. Memoization saves an answer and reuses it until the inputs change. Ask for 7 × 9 and you work that one out fresh.
Watch it happen
Who re-renders, and who skips
1/6You click Toggle theme. setDark changes state in Courses.
The big ideas
What you need to know
useMemo saves a value
useMemo(() => heavyCalc(list), [list]) recalculates only when list changes.
useCallback saves a function
It keeps the same function between renders. Useful when you pass handlers to memoized children.
memo skips a child
Wrap a component in memo() and it skips re-rendering when its props are the same as last time.
React Compiler can do it for you
The React Compiler adds memoization automatically at build time. Either way, measure first before hand-tuning.
See it in code
Walk through the code
1import { memo, useMemo, useState } from "react";2const BigList = memo(function BigList({ items }: { items: string[] }) {3 return <ul>{items.map((c) => <li key={c}>{c}</li>)}</ul>;4});5export default function Courses({ all }: { all: string[] }) {6 const [query, setQuery] = useState("");7 const [dark, setDark] = useState(false);8 const filtered = useMemo(() => all.filter((c) => c.includes(query)), [all, query]);9 return (10 <div className={dark ? "dark" : ""}>11 <input value={query} onChange={(e) => setQuery(e.target.value)} />12 <button onClick={() => setDark(!dark)}>Toggle theme</button>13 <BigList items={filtered} />14 </div>15 );16}1/4memo wraps BigList, so it skips renders when its props haven't changed.
Toggling the theme re-renders Courses, but filtered stays the same array, so memo lets BigList skip its render. Typing changes query, so both update.
Save yourself the pain
Common mistakes
Oops
Wrapping every value in useMemo and every function in useCallback.
Do this instead
Memoization has its own cost. Use it where the Profiler shows a slow render, or let the React Compiler handle it.
Oops
Using memo on a child but passing it a fresh object or arrow function every render.
Do this instead
New objects fail the props check every time. Memoize those props with useMemo or useCallback, or memo does nothing.
Your mission
Find a wasted render
Quick check
BigList is wrapped in memo, but the parent passes items={all.filter(...)} without useMemo. Does BigList skip re-renders?
Go deeper
- 1Wrap an expensive calculation in `useMemo` and confirm it skips recomputation.
- 2Use `React.memo` on a child component to prevent unnecessary re-renders.
- 3Learn that premature memoization can hurt more than help — profile first.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.