An expensive calculation sitting directly in a component's render logic reruns on every single render, even one triggered by something completely unrelated. The useMemo hook lets you cache that calculation's result and skip recomputing it until its actual inputs change.
1Performance Optimization
React is generally fast ā when state changes, it recalculates the virtual DOM and patches the real DOM efficiently. But if a component runs an expensive calculation, like looping through thousands of items or running a heavy regular expression, directly in its render body, that calculation reruns on every re-render, even one triggered by a totally unrelated state change like a text input update.
// useMemo: Cache for expensive logicRender Blockers
Heavy CPU usage freezes UI
2What is Memoization?
Memoization is a general technique for speeding up programs by caching the result of a computation. Once a memoized function calculates an answer for a given input, it saves that result; the next time it's asked for the same input, it skips the calculation entirely and returns the cached answer instead.
import { useMemo } from 'react';
const value = useMemo(() => {
return expensiveTask(data);
}, [data]);The Cache
Remembering past calculations
3The useMemo Hook
The useMemo hook implements this caching pattern in React. It takes an arrow function containing the expensive calculation and a dependency array; React runs that function once, stores the returned value, and hands back the cached value on every subsequent render instead of recalculating it.
// Data same? -> Return cached result
// Data changed? -> Re-run and cache new result4The Dependency Array
The dependency array passed as the second argument tells React exactly when the cache should be considered stale. If every value in that array is the same as the previous render, useMemo returns the cached value; if any of them changed, it reruns the function, caches the new result, and returns that instead.
const filteredList = useMemo(() => {
return list.filter(item => item.match(query));
}, [list, query]);5Example: Heavy Filtering
A classic example is a search bar filtering a large list of users. If the component also has an unrelated piece of state, like a dark mode toggle, toggling that state triggers a re-render ā and without useMemo, the component would needlessly re-filter the entire user list even though neither users nor the search query changed. Wrapping the filter in useMemo with [users, query] as dependencies skips that redundant work.
// Small task? skip useMemo.
// Large loop/API-transform? use it.Theme changes? Filter Skipped.
Query changes? Filter Runs.
6The Danger of Overuse
useMemo isn't free ā creating the closure, allocating memory for the cache, and comparing the dependency array all cost CPU time on every render. Wrapping a trivial calculation like a + b in useMemo actually costs more than just recalculating it directly, so it should be reserved for genuinely expensive operations, not applied reflexively to every value in a component.
const options = useMemo(() => ({ color: 'blue' }), []);Optimization Tax
Cache logic costs CPU too.
7Referential Integrity
Beyond caching expensive calculations, useMemo has a second major use: referential integrity. In JavaScript, primitive values like numbers and strings compare by value, but objects and arrays compare by memory reference ā two freshly created objects with identical contents, like {} === {}, are never equal, because they live at different memory addresses.
<Child options={options} /> // Options is now stableMemory Addresses
0x9A4 != 0x7B2
8Passing Objects as Props
This matters in React because an object literal created inside a component body, like const config = { theme: 'dark' }, gets a brand-new memory address on every single render. If that object is passed down as a prop, the child component sees a 'new' value on every parent re-render ā even though its actual contents never changed ā and re-renders unnecessarily as a result.
/* Memo Lab: Big List Filtering vs UI Updates Rendered */Child Component Panic
Prop changed! Must Re-Render!
9The Object Literal Trap
Writing an object or array literal directly inline inside JSX props, like <HeavyChart config={{ scale: 2 }} />, is known as the object literal trap: it guarantees a brand-new object on every render, which forces any child relying on prop identity ā like a React.memo-wrapped component ā to re-render every time regardless of whether the values actually changed. This is exactly the kind of case useMemo exists to fix.
/* Next: Callback Logic (useCallback) */ā ļø Performance Hazard
10useMemo Is Not a Guarantee
useMemo is a performance hint, not a correctness contract. React is technically free to discard a cached value and recompute it in certain situations, such as a component kept offscreen. Code should never rely on useMemo to guarantee a computation runs only once ā only wrap pure, side-effect-free calculations that would produce the exact same result whether cached or freshly recomputed.
// Wrong mental model: 'this only runs once, guaranteed'
// Correct: 'this is CACHED as an optimization'
const value = useMemo(() => compute(a, b), [a, b]);Not a Contract
useMemo may re-run even with unchanged deps.
11Step-by-Step Breakdown
Performance Optimization. Welcome to Performance Optimization with useMemo. React is generally very fast. When state changes, it recalculates the Virtual DOM and updates the real DOM. But what happens if your component contains a loop that iterates a million times, or runs a massive regular expression? If that component re-renders because of a simple text input change, your entire app will freeze while that math runs again.
What is Memoization?. Memoization is a computer science technique for speeding up programs. It works by keeping a 'cache' (a temporary memory) of previous results. If you ask a memoized function to calculate 5 + 5, it does the math, gets 10, and saves it. If you ask it for 5 + 5 again a second later, it skips the math entirely and just hands you the 10 from its cache.
The useMemo Hook. In React, we use the useMemo hook to implement this cache. It takes two arguments: an arrow function containing the expensive calculation, and a dependency array. React will run your arrow function, store the return value in memory, and give it back to you. On the next render, it won't run the function again; it will just give you the cached value.
The Dependency Array. The magic is in the second argument: the dependency array [data]. This tells React exactly WHEN it needs to throw away the cache and re-run the math. If data is exactly the same as the last render, useMemo returns the cache. If data has changed, useMemo says 'Ah, my cache is stale!', runs the function to get a new answer, caches the new answer, and returns it.
What does useMemo do when a component re-renders, but the variables inside its dependency array have NOT changed since the last render?
- āIt re-runs the function just to be safe
- āIt skips the function and returns the cached value
Example: Heavy Filtering. A classic example is a search bar. You have an array of 10,000 users. The user is typing a search query, but you also have a completely unrelated state variable for 'Dark Mode'. If you toggle Dark Mode, the component re-renders, and without useMemo, it would re-filter those 10,000 users for no reason! Wrapping the filter in useMemo saves the CPU.
In the filtering example, which array tells React exactly when it must throw away the cache and re-filter the data?
- ā[users, query]
- ā[]
The Danger of Overuse. Do NOT wrap everything in useMemo! Memoization is not free. Creating the closure function, allocating memory for the cache, and comparing the dependency array all cost CPU time. If you use useMemo on a trivial calculation like a + b, the process of managing the cache is actually slower than just doing the math again! Only use it for truly heavy tasks.
True or False: Wrapping every single calculation in your React app with useMemo will make it run faster.
- āTrue
- āFalse
Referential Integrity. Beyond performance, useMemo has a second, equally important use: Referential Integrity. In JavaScript, primitives like strings compare by value ('a' === 'a'). But Objects and Arrays compare by memory reference. A newly created array [] is NEVER equal to another array [] because they point to different memory locations.
Passing Objects as Props. Why does this matter in React? If you create an object inside a component like const config = { theme: 'dark' }, React creates a BRAND NEW object in a new memory address every single render. If you pass this config object to a child component, the child sees a new memory address, thinks the prop has changed, and completely re-renders itself! Even if the text inside the object is identical.
The Object Literal Trap. This is known as the 'Object Literal Trap'. Writing {{ color: 'red' }} directly inside JSX props is dangerous if the child is heavy (like a chart or a map). You are guaranteeing that the heavy child will re-render every time the parent re-renders, destroying the child's performance optimizations.
Fixing Identity with useMemo. We fix this using useMemo. By wrapping the object creation in useMemo, we force React to keep the exact same memory address across renders (until dependencies change). Now, when we pass config to the child, the child checks the memory address, sees it hasn't changed, and safely skips re-rendering! We've achieved Referential Stability.
Why might you use useMemo on a tiny, fast object like { theme: 'dark' }?
- āTo speed up the creation of the object
- āTo keep the memory reference identical so children don't re-render
useMemo Is Not a Guarantee. One subtlety worth knowing: useMemo is a performance hint, not a correctness contract. React is technically free to discard a cached value and recompute it in certain situations, like a component kept offscreen. Never rely on useMemo to guarantee a computation runs only once ā only wrap pure, side-effect-free calculations that would produce the exact same result whether cached or not.
Mastery Achieved. Performance mastery achieved! You now know how to deploy useMemo. You understand how to cache heavy algorithms with dependency arrays, the dangers of over-memoizing trivial math, and how to weaponize useMemo to maintain referential integrity for object props. Your React apps will now be lightning fast.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Don't Let Memoization Delay Accessible Feedback
If an expensive calculation wrapped in `useMemo` drives visible content like a live results count, make sure the dependency array is correct ā a stale memoized value can leave an `aria-live` region announcing outdated information to screen reader users.
2Heavy Client-Side Filtering Still Needs a Loading or Empty State
A `useMemo`-optimized filter over a large dataset can still take a noticeable moment on low-end devices ā pair it with a properly announced loading or 'no results' state so users relying on assistive technology aren't left waiting without feedback.
SEO Implications
- 1
useMemo Has No Direct Effect on Server-Rendered HTML
Memoization only affects client-side re-renders after hydration; it doesn't change what's present in the initial server-rendered markup a crawler sees, so it shouldn't be relied on for anything related to what content is indexable.
- 2
Faster Interactions After Hydration Support Core Web Vitals
Eliminating unnecessary heavy recalculations with useMemo keeps the main thread free, which helps metrics like Interaction to Next Paint stay low ā a factor that can indirectly influence search ranking through Core Web Vitals.
Best Practices
Reserve useMemo for Genuinely Expensive Calculations
Only wrap operations with real, measurable cost ā large loops, heavy filtering/sorting, complex data transforms ā in useMemo; wrapping trivial arithmetic or string concatenation adds overhead without any benefit.
Use useMemo to Stabilize Object and Array References Passed as Props
When passing an object or array to a child wrapped in React.memo, wrap its creation in useMemo with the correct dependency array so the reference stays stable across renders where the underlying data hasn't changed.
Frequent Bugs
A component wrapped in React.memo still re-renders every time its parent re-renders, even though the memoized prop's contents look unchanged.
The parent is creating a new object or array literal inline in JSX on every render, giving it a new memory reference each time. Wrap the object's creation in `useMemo` with the correct dependency array so the reference stays stable.
Adding useMemo to a component made it measurably slower instead of faster.
The wrapped calculation was trivial ā the overhead of creating the closure and comparing the dependency array on every render exceeded the cost of just recalculating the value directly. Remove useMemo from calculations that aren't actually expensive.
Real-World Examples
Memoizing a Filtered Product List
An e-commerce page filters a 10,000-item product list based on a search query. The page also has an unrelated dark mode toggle; without useMemo, toggling dark mode would re-run the expensive filter for no reason, so the filter is wrapped in useMemo with [products, query] as its dependencies.
const filteredProducts = useMemo(() => {
return products.filter(p => p.name.includes(query));
}, [products, query]);