useMemo vs. useCallback: A pragmatic guide
Editor’s note: This guide to React useMemo vs. useCallback was last updated by Shalitha Suranga on 18 May 2023 to reflect recent changes to React and address how to improve performance in your React apps with useMemo and useCallback.
As HTML pages grow in size and complexity, creating efficient React code becomes more important than ever. Re-rendering large components is costly, and providing a significant amount of work for the browser through a single-page application (SPA) increases processing time and can potentially drive away users. React offers inbuilt API features to improve app performance by avoiding unnecessary re-renders, caching repetitive costly operations, lazy-loading components, etc.
This tutorial examines two different React Hooks, useMemo and useCallback. These Hooks help developers improve the rendering performance of components, preserve objects between React component renderings, and help improve application performance.
Jump ahead:
useCallback?
useMemo?
useMemo and useCallbackuseCallback vs. useMemo in ReactuseMemo and useCallback
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
React already provides React.memo() to avoid recreating DOM elements when props are not changed. This method is a higher-order component (HOC) that memoizes the last result. But, it doesn’t memoize typical JavaScript functions. Therefore, despite being a first-class citizen in JavaScript, functions may potentially be recreated with every use.
The useMemo and useCallback methods help to avoid recreating or rerunning functions in certain situations. Although not always useful, useMemo or useCallback may create a noticeable difference when dealing with large amounts of data or many components that share behavior. For example, this would be especially useful when you’re creating a stock or digital currency trading platform.
useCallback?When React re-renders a component, function references inside the component get re-created. If you pass a callback function to a memoized (with React.memo) child component via props, it may get re-rendered even if the parent component doesn’t apparently change the child component’s props. Each parent component re-rendering phase creates new function references for callbacks, so inequal callback props can trigger an unwanted child component re-render silently even visible props don’t get changed.
The useCallback React Hook returns a memoized function reference based on a function and dependencies array. So, we can use it to create optimized callbacks that don’t cause unwanted re-renders. This Hook returns a cached (memoized) function reference if dependencies aren’t changed.
useCallbackNow, let’s get started. First, create a React project on your computer with Create React App to follow along:
npx create-react-app PerformanceHooks
Or, you can see or edit the upcoming examples in provided CodeSandbox links. Now, look at the following example source code that passes a callback function to a memoized child component:
import { memo, useState } from "react";
import "./styles.css";
const Numbers = memo(({ nums, addRandom }) => {
console.log("Numbers rendered");
return (
<div>
<ul>
{nums.map((num, i) => (
<li key={i}>{num}</li>
))}
</ul>
<button onClick={addRandom}>Add random</button>
</div>
);
});
export default function App() {
const [nums, setNums] = useState([]);
const [count, setCount] = useState(0);
const increaseCounter = () => {
setCount(count + 1);
};
const addRandom = () => {
let randNum = parseInt(Math.random() * 1000, 10);
setNums([...nums, randNum]);
};
return (
<div>
<div>
Count: {count}
<button onClick={increaseCounter}>+</button>
</div>
<hr />
<Numbers nums={nums} addRandom={addRandom} />
</div>
);
}
The above source code renders a counter in the App component and a random number list in the Numbers component. React will re-render the Numbers component when the counter increases, even if it’s optimized with memo, as shown below:

The reason is that whenever the App component re-renders, it re-creates a function reference for the addRandom callback. The Numbers component gets re-rendered since the props are different! As a solution, we can wrap addRandom with useCallback, as shown in the following code snippet:
const addRandom = useCallback(() => {
let randNum = parseInt(Math.random() * 1000, 10);
setNums([...nums, randNum]);
}, [nums]);
The above solution eliminates the previously discussed unwanted re-render since addRandom receives a cached function reference.
useCallbackSee the following table to understand the pros and cons of using useCallback as a performance enhancement in your React apps:
useCallback pros |
useCallback cons |
|---|---|
| Helps developers cache a function to avoid excessive re-renders of a child component | Adds excessive syntax for callback definition, so use of useCallback may complicate your code |
Developers can improve the use of memo built-in’s performance enhancements (See the previous example) |
Cannot be used to efficiently and properly cache a value as useMemo |
| Comes as an inbuilt, stable React core feature that we can use in production | The usage of this Hook may confuse React newcomers since it caches a function — not a simple value |
| Offers an easy function interface that accepts just two parameters: a function and dependencies array | Excessive usage can lead to memory-related performance issues |
useMemo?In some scenarios, we have to include complex calculations in our React components. These complex calculations are inevitable and may slow down the rendering flow a bit. If you had to re-render a component that handles a costly calculation to update another view result (not the result of the costly calculation), the costly calculation may get triggered again, ultimately causing performance issues. This situation can be solved by caching the complex calculation result.
The useMemo Hook serves a similar purpose as useCallback, but it returns a memoized value instead of a function reference. This allows you to avoid repeatedly performing potentially costly operations until necessary. The useMemo Hook typically returns a cached value until a dependency gets changed. If a dependency gets changed, React will re-do the expensive calculation and updates the memoized value.
useMemoLook at the following source code that does a costly calculation with one state field:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [nums, setNums] = useState([]);
const [count, setCount] = useState(1);
const increaseCounter = () => {
setCount(count + 1);
};
const addRandom = () => {
let randNum = parseInt(Math.random() * 1000, 10);
setNums([...nums, randNum]);
};
const magicNum = calculateMagicNumber(count);
return (
<div>
<div>
Counter: {count} | Magic number: {magicNum}
<button onClick={increaseCounter}>+</button>
</div>
<hr />
<div>
<ul>
{nums.map((num, i) => (
<li key={i}>{num}</li>
))}
</ul>
<button onClick={addRandom}>Add random</button>
</div>
</div>
);
}
function calculateMagicNumber(n) {
console.log("Costly calculation triggered.");
let num = 1;
for (let i = 0; i < n + 1000000000; i++) {
num += 123000;
}
return parseInt(num - num * 0.22, 10);
}
The above code implements two main functional segments in the app:
Once you run the app, it will work as expected. It will calculate a magic number for the current counter value and will add new random numbers to the list when you click the Add random button. Magic number calculation is costly, so you’ll feel a delay once you increase the counter value.
There is a hidden issue. Why does the Add random button work so slowly as the magic number generation process? Look at the following preview:

The reason is that the Add random button also triggers a mandatory re-render which triggers the calculateMagicNumber slow function. As a solution, we can wrap the calculateMagicNumber function call with useMemo to let React use a cache value when the App component re-renders via the Add random button:
const magicNum = useMemo(() => calculateMagicNumber(count), [count]);
Now, the useMemo Hook calculates a new magic number only if the count dependency gets changed, so the Add random will work faster! You can check out the optimized example here:
useMemoSee the following table to understand the pros and cons of using useMemo as a performance enhancement in your React apps:
useMemo pros |
useMemo cons |
|---|---|
| Helps developers cache a value to avoid unwanted costly recalculations | Adds excessive syntax for compute function calls, so use of useMemo may complicate your code |
Able to use with inbuilt memo when a child component uses a computed object that doesn’t need frequent re-computations |
Can be used to cache a function, but it affects readability (Use useCallback for caching functions) |
| Comes as an inbuilt, stable React core feature that we can use in production | React newcomers may use this Hook for situations where caching isn’t needed, such as with simple calculations, frequently changed values, etc. As a result, code readability and app memory usage will get affected |
| Offers an easy function interface that accepts just two parameters: a function and dependencies array | Excessive usage can lead to memory-related performance issues |
useMemo and useCallbackA React library often needs to check the equality of two identifiers. For example, when you update a component state field, React needs to check whether the previous state field is not equal to the current one before triggering a new re-render. Similarly, useMemo and useCallback needs to check the equality of identifiers for invaliding the cached items.
In JavaScript, equality checking for primitives is straightforward because they are immutable (cannot be changed without creating a new one). But, objects and functions are mutable. If React used deep comparison for objects and functions, there will be performance drawbacks. So, React uses JavaScript’s referential equality concept for comparing two identifiers.
Referential equality refers to comparing identifiers based on their references. For example, the following code snippet prints true two times since object and function references are equal:
let a = {msg: 'Hello'};
let b = a;
b.msg = 'World';
console.log(a === b); // true
let c = () => {};
let d = c;
console.log(c === d); // true
The following code snippet prints false two times even if identifier data look the same:
let a = {msg: 'Hello'};
let b = {msg: 'Hello'};
console.log(a === b); // false
let c = () => 100;
let d = () => 100;
console.log(c === d); // false
The above code snippet prints false two times since identifier references are different. React internally uses this referential equality check via the Object.is() method (works the same as === as we tested before) to detect changes between states.
When we don’t use useCallback, React triggered an unwanted re-render since referential equality was false as the callback gets re-created in every re-render. Similarly, if we create a complex object in a component via an expensive function without useMemo, it will slow down all re-renders since referential equality becomes false. Both useMemo and useCallback let you set referential quality to true by returning cached unchanged references.
useCallback vs. useMemo in ReactThe useCallback and useMemo Hooks appear similar on the surface. However, there are particular use cases for each.
Wrap functions with useCallback when:
React.memo()-wrapped component accepts a callback function as a propuseEffect)Use useMemo when:
A callback works well when code would otherwise be recompiled with every call. Memoizing results can help decrease the cost of repeatedly calling functions when the inputs change gradually over time. On the other hand, in the trading example, we may not want to memoize results for a constantly changing order book.
useMemo and useCallbackIf your React app triggers excessive, unwanted re-renders and has slow processing before each re-render, it may use more CPU and memory. This situation won’t be noticeable for users through small apps. But, large apps that have critical rendering performance issues can slow down users’ computers, reducing usability factors and product quality. React helps you solve critical rendering-related performance issues with useMemo and useCallback.
Here is a checklist that you can follow to improve React app performance with useMemo and useCallback:
console.time(), or React Profiler. Detect unwanted re-rendersuseMemo or useCallbackOverusing useMemo and useCallback may worsen existing performance issues, so let’s explain the anti-patterns below.
useCallback and useMemo anti-patternsIt can be tempting to think you can use useCallback or useMemo for every function. However, this is not the case. There is overhead associated with wrapping functions. Each call requires extra work to unravel your function call and decide how to proceed.
Notice how the increaseCounter function is not a callback that we need to add useCallback while the addRandom function is. The increaseCounter function does not meet our criteria, as it is created once and never shared with child components.
In contrast, each changing list item in the Number component uses the addRandom function. Similarly, we wrapped the calculateMagicNumber function call using useMemo, but would never wrap functions that deal with frequently changing data with useMemo.
The useCallback and useMemo functions are instruments for fine-tuning React. Knowing how and when to use each could potentially improve application performance. Still, no inbuilt performance-improvement Hook is a substitute for a poorly written React app codebase. Here, we’ve provided a guide for understanding how to use these tools, but keep in mind that using them comes with a cost (memory usage for caching).
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

Vibe coding isn’t just AI-assisted chaos. Here’s how to avoid insecure, unreadable code and turn your “vibes” into real developer productivity.

GitHub SpecKit brings structure to AI-assisted coding with a spec-driven workflow. Learn how to build a consistent, React-based project guided by clear specs and plans.

:has(), with examplesThe CSS :has() pseudo-class is a powerful new feature that lets you style parents, siblings, and more – writing cleaner, more dynamic CSS with less JavaScript.

Kombai AI converts Figma designs into clean, responsive frontend code. It helps developers build production-ready UIs faster while keeping design accuracy and code quality intact.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now
One Reply to "React <code>useMemo</code> vs. <code>useCallback</code>: A pragmatic guide"
Thanks for the article. You mentioned trader for this example. I wonder how much time it could save here, maybe less than 1ms, my guts feeling, which means this has to be very high frequency trader 🙂