memo lets you skip re-rendering a component when its props are unchanged.
constMemoizedComponent =memo(SomeComponent,arePropsEqual?)Note
React Compiler automatically applies the equivalent ofmemo to all components, reducing the need for manual memoization. You can use the compiler to handle component memoization automatically.
Reference
memo(Component, arePropsEqual?)
Wrap a component inmemo to get amemoized version of that component. This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed. But React may still re-render it: memoization is a performance optimization, not a guarantee.
import{memo}from'react';
constSomeComponent =memo(functionSomeComponent(props){
// ...
});Parameters
Component: The component that you want to memoize. Thememodoes not modify this component, but returns a new, memoized component instead. Any valid React component, including functions andforwardRefcomponents, is accepted.optional
arePropsEqual: A function that accepts two arguments: the component’s previous props, and its new props. It should returntrueif the old and new props are equal: that is, if the component will render the same output and behave in the same way with the new props as with the old. Otherwise it should returnfalse. Usually, you will not specify this function. By default, React will compare each prop withObject.is.
Returns
memo returns a new React component. It behaves the same as the component provided tomemo except that React will not always re-render it when its parent is being re-rendered unless its props have changed.
Usage
Skipping re-rendering when props are unchanged
React normally re-renders a component whenever its parent re-renders. Withmemo, you can create a component that React will not re-render when its parent re-renders so long as its new props are the same as the old props. Such a component is said to bememoized.
To memoize a component, wrap it inmemo and use the value that it returns in place of your original component:
constGreeting =memo(functionGreeting({name}){
return<h1>Hello,{name}!</h1>;
});
exportdefaultGreeting;A React component should always havepure rendering logic. This means that it must return the same output if its props, state, and context haven’t changed. By usingmemo, you are telling React that your component complies with this requirement, so React doesn’t need to re-render as long as its props haven’t changed. Even withmemo, your component will re-render if its own state changes or if a context that it’s using changes.
In this example, notice that theGreeting component re-renders whenevername is changed (because that’s one of its props), but not whenaddress is changed (because it’s not passed toGreeting as a prop):
import{memo,useState}from'react';exportdefaultfunctionMyApp(){const[name,setName] =useState('');const[address,setAddress] =useState('');return(<><label> Name{': '}<inputvalue={name}onChange={e=>setName(e.target.value)}/></label><label> Address{': '}<inputvalue={address}onChange={e=>setAddress(e.target.value)}/></label><Greetingname={name}/></>);}constGreeting =memo(functionGreeting({name}){console.log("Greeting was rendered at",newDate().toLocaleTimeString());return<h3>Hello{name &&', '}{name}!</h3>;});
Note
You should only rely onmemo as a performance optimization. If your code doesn’t work without it, find the underlying problem and fix it first. Then you may addmemo to improve performance.
Deep Dive
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 withmemo is only valuable when your component re-renders often with the same exact props, and its re-rendering logic is expensive. If there is no perceptible lag when your component re-renders,memo is unnecessary. Keep in mind thatmemo is completely useless if the props passed to your component arealways different, such as if you pass an object or a plain function defined during rendering. This is why you will often needuseMemo anduseCallback together withmemo.
There is no benefit to wrapping a component inmemo 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:
- When a component visually wraps other components, let itaccept JSX as children. This way, when the wrapper component updates its own state, React knows that its children don’t need to re-render.
- Prefer local state and don’tlift state up 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.
- Keep yourrendering logic 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.
- Avoidunnecessary Effects that update state. Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.
- Try toremove unnecessary dependencies from your Effects. 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 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 researchingdoing granular memoization automatically to solve this once and for all.
Updating a memoized component using state
Even when a component is memoized, it will still re-render when its own state changes. Memoization only has to do with props that are passed to the component from its parent.
import{memo,useState}from'react';exportdefaultfunctionMyApp(){const[name,setName] =useState('');const[address,setAddress] =useState('');return(<><label> Name{': '}<inputvalue={name}onChange={e=>setName(e.target.value)}/></label><label> Address{': '}<inputvalue={address}onChange={e=>setAddress(e.target.value)}/></label><Greetingname={name}/></>);}constGreeting =memo(functionGreeting({name}){console.log('Greeting was rendered at',newDate().toLocaleTimeString());const[greeting,setGreeting] =useState('Hello');return(<><h3>{greeting}{name &&', '}{name}!</h3><GreetingSelectorvalue={greeting}onChange={setGreeting}/></>);});functionGreetingSelector({value,onChange}){return(<><label><inputtype="radio"checked={value ==='Hello'}onChange={e=>onChange('Hello')}/> Regular greeting</label><label><inputtype="radio"checked={value ==='Hello and welcome'}onChange={e=>onChange('Hello and welcome')}/> Enthusiastic greeting</label></>);}
If you set a state variable to its current value, React will skip re-rendering your component even withoutmemo. You may still see your component function being called an extra time, but the result will be discarded.
Updating a memoized component using a context
Even when a component is memoized, it will still re-render when a context that it’s using changes. Memoization only has to do with props that are passed to the component from its parent.
import{createContext,memo,useContext,useState}from'react';constThemeContext =createContext(null);exportdefaultfunctionMyApp(){const[theme,setTheme] =useState('dark');functionhandleClick(){setTheme(theme ==='dark' ?'light' :'dark');}return(<ThemeContextvalue={theme}><buttononClick={handleClick}> Switch theme</button><Greetingname="Taylor"/></ThemeContext>);}constGreeting =memo(functionGreeting({name}){console.log("Greeting was rendered at",newDate().toLocaleTimeString());consttheme =useContext(ThemeContext);return(<h3className={theme}>Hello,{name}!</h3>);});
To make your component re-render only when apart of some context changes, split your component in two. Read what you need from the context in the outer component, and pass it down to a memoized child as a prop.
Minimizing props changes
When you usememo, your component re-renders whenever any prop is notshallowly equal to what it was previously. This means that React compares every prop in your component with its previous value using theObject.is comparison. Note thatObject.is(3, 3) istrue, butObject.is({}, {}) isfalse.
To get the most out ofmemo, minimize the times that the props change. For example, if the prop is an object, prevent the parent component from re-creating that object every time by usinguseMemo:
functionPage(){
const[name,setName] =useState('Taylor');
const[age,setAge] =useState(42);
constperson =useMemo(
()=>({name,age}),
[name,age]
);
return<Profileperson={person}/>;
}
constProfile =memo(functionProfile({person}){
// ...
});A better way to minimize props changes is to make sure the component accepts the minimum necessary information in its props. For example, it could accept individual values instead of a whole object:
functionPage(){
const[name,setName] =useState('Taylor');
const[age,setAge] =useState(42);
return<Profilename={name}age={age}/>;
}
constProfile =memo(functionProfile({name,age}){
// ...
});Even individual values can sometimes be projected to ones that change less frequently. For example, here a component accepts a boolean indicating the presence of a value rather than the value itself:
functionGroupsLanding({person}){
consthasGroups =person.groups !==null;
return<CallToActionhasGroups={hasGroups}/>;
}
constCallToAction =memo(functionCallToAction({hasGroups}){
// ...
});When you need to pass a function to memoized component, either declare it outside your component so that it never changes, oruseCallback to cache its definition between re-renders.
Specifying a custom comparison function
In rare cases it may be infeasible to minimize the props changes of a memoized component. In that case, you can provide a custom comparison function, which React will use to compare the old and new props instead of using shallow equality. This function is passed as a second argument tomemo. It should returntrue only if the new props would result in the same output as the old props; otherwise it should returnfalse.
constChart =memo(functionChart({dataPoints}){
// ...
},arePropsEqual);
functionarePropsEqual(oldProps,newProps){
return(
oldProps.dataPoints.length ===newProps.dataPoints.length &&
oldProps.dataPoints.every((oldPoint,index)=>{
constnewPoint =newProps.dataPoints[index];
returnoldPoint.x ===newPoint.x &&oldPoint.y ===newPoint.y;
})
);
}If you do this, use the Performance panel in your browser developer tools to make sure that your comparison function is actually faster than re-rendering the component. You might be surprised.
When you do performance measurements, make sure that React is running in the production mode.
Pitfall
If you provide a customarePropsEqual implementation,you must compare every prop, including functions. Functions oftenclose over the props and state of parent components. If you returntrue whenoldProps.onClick !== newProps.onClick, your component will keep “seeing” the props and state from a previous render inside itsonClick handler, leading to very confusing bugs.
Avoid doing deep equality checks insidearePropsEqual unless you are 100% sure that the data structure you’re working with has a known limited depth.Deep equality checks can become incredibly slow and can freeze your app for many seconds if someone changes the data structure later.
Do I still need React.memo if I use React Compiler?
When you enableReact Compiler, you typically don’t needReact.memo anymore. The compiler automatically optimizes component re-rendering for you.
Here’s how it works:
Without React Compiler, you needReact.memo to prevent unnecessary re-renders:
// Parent re-renders every second
functionParent(){
const[seconds,setSeconds] =useState(0);
useEffect(()=>{
constinterval =setInterval(()=>{
setSeconds(s=>s +1);
},1000);
return()=>clearInterval(interval);
},[]);
return(
<>
<h1>Seconds:{seconds}</h1>
<ExpensiveChildname="John"/>
</>
);
}
// Without memo, this re-renders every second even though props don't change
constExpensiveChild =memo(functionExpensiveChild({name}){
console.log('ExpensiveChild rendered');
return<div>Hello,{name}!</div>;
});With React Compiler enabled, the same optimization happens automatically:
// No memo needed - compiler prevents re-renders automatically
functionExpensiveChild({name}){
console.log('ExpensiveChild rendered');
return<div>Hello,{name}!</div>;
}Here’s the key part of what the React Compiler generates:
functionParent(){
const$ =_c(7);
const[seconds,setSeconds] =useState(0);
// ... other code ...
lett3;
if($[4] ===Symbol.for("react.memo_cache_sentinel")){
t3 =<ExpensiveChildname="John"/>;
$[4] =t3;
}else{
t3 =$[4];
}
// ... return statement ...
}Notice the highlighted lines: The compiler wraps<ExpensiveChild name="John" /> in a cache check. Since thename prop is always"John", this JSX is created once and reused on every parent re-render. This is exactly whatReact.memo does - it prevents the child from re-rendering when its props haven’t changed.
The React Compiler automatically:
- Tracks that the
nameprop passed toExpensiveChildhasn’t changed - Reuses the previously created JSX for
<ExpensiveChild name="John" /> - Skips re-rendering
ExpensiveChildentirely
This meansyou can safely removeReact.memo from your components when using React Compiler. The compiler provides the same optimization automatically, making your code cleaner and easier to maintain.
Note
The compiler’s optimization is actually more comprehensive thanReact.memo. It also memoizes intermediate values and expensive computations within your components, similar to combiningReact.memo withuseMemo throughout your component tree.
Troubleshooting
My component re-renders when a prop is an object, array, or function
React compares old and new props by shallow equality: that is, it considers whether each new prop is reference-equal to the old prop. If you create a new object or array each time the parent is re-rendered, even if the individual elements are each the same, React will still consider it to be changed. Similarly, if you create a new function when rendering the parent component, React will consider it to have changed even if the function has the same definition. To avoid this,simplify props or memoize props in the parent component.