---
title: useMemo
---
`useMemo` is a React Hook that lets you cache the result of a calculation between re-renders.
```js
const memoizedValue = useMemo(calculateValue, dependencies)
```
---
## Usage {/*usage*/}
### Skipping expensive recalculations {/*skipping-expensive-recalculations*/}
By default, React will re-run the entire body of your component every time that it re-renders. For example, if this `TodoList` updates its state or receives new props from its parent, the `filterTodos` function will re-run:
```js {2}
function TodoList({ todos, tab, theme }) {
const visibleTodos = filterTodos(todos, tab);
// ...
}
```
**Usually, this isn't a problem because most calculations are very fast.** However, if you're filtering or transforming a large array, or doing some expensive computation, you might want to skip doing it again if data hasn't changed. If both `todos` and `tab` are the same as they were during the last render, you can instruct React to reuse the `visibleTodos` you've already calculated during the last render. This type of caching is called *[memoization.](https://en.wikipedia.org/wiki/Memoization)*
**To cache a value between re-renders, wrap its calculation in a `useMemo` call at the top level of your component:**
```js [[3, 4, "visibleTodos"], [1, 4, "() => filterTodos(todos, tab)"], [2, 4, "[todos, tab]"]]
import { useMemo } from 'react';
function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
}
```
You need to pass two things to `useMemo`:
1. A calculation function that takes no arguments, like `() =>`, and returns what you wanted to calculate.
2. A list of dependencies including every value within your component that's used inside your calculation.
On the initial render, the value you'll get from `useMemo` will be the result of calling your calculation.
On every subsequent render, React will compare the dependencies with the dependencies you passed during the last render. If none of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useMemo` will return the value you already calculated on the last render. Otherwise, React will re-run your calculation and return the new value.
In other words, `useMemo` will cache your function's result, and return it on re-renders until the dependencies change. If both `todos` and `tab` are the same as before, the `TodoList` won't have to recalculate `visibleTodos`.
**You should only rely on `useMemo` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `useMemo` to improve performance.
In general, unless you're creating or looping over thousands of objects, it's probably not expensive. If you want to get more confidence, you can add a console log to measure the time spent in a piece of code:
```js {1,3}
console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');
```
Perform the interaction you're measuring (for example, typing into the input). You will then see logs like `filter array: 0.15ms` in your console. If the overall logged time adds up to a significant amount (say, `1ms` or more), it might make sense to memoize that calculation. As an experiment, you can then wrap the calculation in `useMemo` to verify whether the total logged time has decreased for that interaction or not:
```js
console.time('filter array');
const visibleTodos = useMemo(() => {
return filterTodos(todos, tab); // Skipped if todos and tab haven't changed
}, [todos, tab]);
console.timeEnd('filter array');
```
`useMemo` won't make the *first* render faster. It only helps you skip unnecessary work on updates.
Keep in mind that your machine is probably faster than your users' so it's a good idea to test the performance with an artificial slowdown. For example, Chrome offers a [CPU Throttling](https://developer.chrome.com/blog/new-in-devtools-61/#throttling) option for this.
Also note that measuring performance in development will not give you the most accurate results. (For example, when [Strict Mode](/apis/react/StrictMode) is on, you will see each component render twice rather than once.) To get the most accurate timings, build your app for production and test it on a device like your users have.
If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful.
Optimizing with `useMemo` is only valuable in a few cases:
- The calculation you're putting in `useMemo` is noticeably slow, and its dependencies rarely change.
- You pass it as a prop to a component wrapped in [`memo`.](/apis/react/memo) You want to skip re-rendering if the value hasn't changed. Memoization lets your component re-render only when dependencies are the same.
- The value you're passing is later used as a dependency of some Hook. For example, maybe another `useMemo` calculation value depends on it. Or maybe you are depending on this value from [`useEffect.`](/apis/react/useEffect)
There is no benefit to wrapping a calculation in `useMemo` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside of this approach is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component.
**In practice, you can make a lot of memoization unnecessary by following a few principles:**
1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) This way, when the wrapper component updates its own state, React knows that its children don't need to re-render.
1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. For example, don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library.
1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization.
1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.
1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component.
If a specific interaction still feels laggy, [use the React Developer Tools profiler](/blog/2018/09/10/introducing-the-react-profiler.html) to see which components would benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In the long term, we're researching [doing granular memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all.
#### Skipping recalculation with `useMemo` {/*skipping-recalculation-with-usememo*/}
In this example, the `filterTodos` implementation is **artificially slowed down** so that you can see what happens when some JavaScript function you're calling during rendering is genuinely slow. Try switching the tabs and toggling the theme.
Switching the tabs feels slow because it forces the slowed down `filterTodos` to re-execute. That's expected because the `tab` has changed, and so the entire calculation *needs* to re-run. (If you're curious why it runs twice, it's explained [here.](#my-calculation-runs-twice-on-every-re-render))
Next, try toggling the theme. **Thanks to `useMemo`, it's fast despite the artificial slowdown!** The slow `filterTodos` call was skipped because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render.
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import { useMemo } from 'react';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
return (
Note: filterTodos is artificially slowed down!
{visibleTodos.map(todo => (
{todo.completed ?
{todo.text} :
todo.text
}
))}
);
}
```
```js utils.js
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
```
```css
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
```
#### Always recalculating a value {/*always-recalculating-a-value*/}
In this example, the `filterTodos` implementation is also **artificially slowed down** so that you can see what happens when some JavaScript function you're calling during rendering is genuinely slow. Try switching the tabs and toggling the theme.
Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the artificially slowed down `filterTodos` gets called on every re-render. It is called even if only `theme` has changed.
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
Note: filterTodos is artificially slowed down!
{visibleTodos.map(todo => (
{todo.completed ?
{todo.text} :
todo.text
}
))}
);
}
```
```js utils.js
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
```
```css
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
```
However, here is the same code **with the artificial slowdown removed.** Does the lack of `useMemo` feel noticeable or not?
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
{visibleTodos.map(todo => (
{todo.completed ?
{todo.text} :
todo.text
}
))}
);
}
```
```js utils.js
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
```
```css
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
```
Quite often, code without memoization works fine. If your interactions are fast enough, you might not need memoization.
You can try increasing the number of todo items in `utils.js` and see how the behavior changes. This particular calculation wasn't very expensive to begin with, but if the number of todos grows significantly, most of the overhead will be in re-rendering rather than in the filtering. Keep reading below to see how you can optimize re-rendering with `useMemo`.
---
### Skipping re-rendering of components {/*skipping-re-rendering-of-components*/}
By default, when a component re-renders, React re-renders all of its children recursively. This is fine for components that don't require much calculation to re-render. Components higher up the tree or slower components can opt into *skipping re-renders when their props are the same* by wrapping themselves in [`memo`](/apis/react/memo):
```js {1,7}
import { memo } from 'react';
function List({ items }) {
// ...
}
export default memo(List);
```
**This is a performance optimization. The `useMemo` and [`useCallback`](/apis/react/useCallback) Hooks are often needed to make it work.**
For this optimization to work, the parent component that renders this `` needs to ensure that, if it doesn't want `List` to re-render, every prop it passes to the `List` must be the same as on the last render.
Let's say the parent `TodoList` component looks like this:
```js {2,5}
export default function TodoList({ todos, tab, theme }) {
const visibleTodos = filterTodos(todos, tab);
return (
);
}
```
With the above code, the `List` optimization will not work because `visibleTodos` will be a different array on every re-render of the `TodoList` component. To fix it, wrap the calculation of `visibleTodos` in `useMemo`:
```js {2,5}
export default function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
return (
);
}
```
After this change, as long as `todos` and `tab` haven't changed, thanks to `useMemo`, the `visibleTodos` won't change between re-renders. Since `List` is wrapped in [`memo`](/apis/react/memo), it will only re-render if one of its props is different from its value on the last render. You're passing the same `items` prop, so `List` can skip the re-rendering entirely.
Notice that in this example, it doesn't matter whether `filterTodos` itself is fast or slow. The point isn't to avoid a *slow calculation,* but it's to avoid *passing a different prop value every time* since that would break the [`memo`](/apis/react/memo) optimization of the child `List` component. The `useMemo` call in the parent makes `memo` work for the child.
Instead of wrapping `List` in [`memo`](/apis/react/memo), you could wrap the `` JSX node itself in `useMemo`:
```js {3,6}
export default function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
const children = useMemo(() => , [visibleTodos]);
return (
{children}
);
}
```
The behavior would be the same. If the `visibleTodos` haven't changed, `List` won't be re-rendered.
A JSX node like `` is an object like `{ type: List, props: { items: visibleTodos } }`. Creating this object is very cheap, but React doesn't know whether its contents is the same as last time or not. This is why by default, React will re-render the `List` component.
However, if React sees the same exact JSX as during the previous render, it won't try to re-render your component. This is because JSX nodes are [immutable.](https://en.wikipedia.org/wiki/Immutable_object) A JSX node object could not have changed over time, so React knows it's safe to skip a re-render. However, for this to work, the node has to *actually be the same object*, not merely look the same in code. This is what `useMemo` does in this example.
Manually wrapping JSX nodes into `useMemo` is not convenient. For example, you can't do this conditionally. This is usually you would wrap components with [`memo`](/apis/react/memo) instead of wrapping JSX nodes.
#### Skipping re-rendering with `useMemo` and `memo` {/*skipping-re-rendering-with-usememo-and-memo*/}
In this example, the `List` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme.
Switching the tabs feels slow because it forces the slowed down `List` to re-render. That's expected because the `tab` has changed, and so you need to reflect the user's new choice on the screen.
Next, try toggling the theme. **Thanks to `useMemo` together with [`memo`](/apis/react/memo), itβs fast despite the artificial slowdown!** The `List` skipped re-rendering because the `visibleItems` array has not changed since the last render. The `visibleItems` array has not changed because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render.
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import { useMemo } from 'react';
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
return (
Note: List is artificially slowed down!
);
}
```
```js List.js
import { memo } from 'react';
function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return (
{items.map(item => (
{item.completed ?
{item.text} :
item.text
}
))}
);
}
export default memo(List);
```
```js utils.js
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
```
```css
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
```
#### Always re-rendering a component {/*always-re-rendering-a-component*/}
In this example, the `List` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme.
Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the `visibleTodos` is always a different array, and the slowed down `List` component can't skip re-rendering.
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
Note: List is artificially slowed down!
);
}
```
```js List.js
import { memo } from 'react';
function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return (
{items.map(item => (
{item.completed ?
{item.text} :
item.text
}
))}
);
}
export default memo(List);
```
```js utils.js
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
```
```css
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
```
However, here is the same code **with the artificial slowdown removed.** Does the lack of `useMemo` feel noticeable or not?
```js App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
```
```js TodoList.js active
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
);
}
```
```js List.js
import { memo } from 'react';
function List({ items }) {
return (