Introduction
"Why is my UserCard component re-rendering 30 times when I scroll through a list, even though the user data hasn't changed?"
I've debugged this exact scenario more times than I can count. React's default behavior is to re-render everything in the component tree whenever the parent updates — even when props remain identical.
After optimizing dozens of production apps, I've learned that React.memo is your first line of defense against unnecessary renders. Here's how to use it effectively.
What is React.memo?
React.memo is a higher-order component that implements a shallow comparison check for your functional components.
- It caches the rendered output for a given set of props
- It only triggers a re-render when props actually change (shallow comparison)
Think of it as React saying: "I've seen these exact props before. Let me reuse the result instead of doing the work again."
Basic Example of React.memo
const UserCard = React.memo(({ user, onEdit }) => {
console.log("UserCard rendered for:", user.name);
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
);
});
Now when the parent component re-renders (say, due to unrelated state changes), UserCard will skip re-rendering if the user object and onEdit function reference haven't changed.
The Problem: Without React.memo
const UserList = () => {
const [searchTerm, setSearchTerm] = useState("");
const [users] = useState(MOCK_USERS); // Static list
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search users..."
/>
{users.map(user => (
<UserCard
key={user.id}
user={user}
onEdit={(id) => console.log("Edit user:", id)}
/>
))}
</div>
);
};
Every keystroke in the search input re-renders ALL UserCard components, even though the user data hasn't changed. In a list of 100+ users, this kills performance.
✅ The Solution: With React.memo
const UserCard = React.memo(({ user, onEdit }) => {
console.log("UserCard rendered for:", user.name);
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
);
});
Now only the search input re-renders on each keystroke. The UserCard components stay memoized because their props haven't changed. I've seen this optimization reduce render time from 150ms to 15ms in production apps.
How Does React.memo Work Under the Hood?
React.memo performs a shallow comparison of the previous and current props:
- ✅ Primitives (strings, numbers, booleans) work perfectly
- ⚠️ Objects and functions need extra care — they're compared by reference, not value
This is why you'll often pair React.memo with useCallback and useMemo to stabilize object and function props.
Real Production Example: Dashboard Card Optimization
const MetricCard = React.memo(({ title, value, trend, onClick }) => {
// This component has expensive chart rendering
const chartData = useMemo(() => generateChartData(trend), [trend]);
console.log("MetricCard rendered:", title);
return (
<div className="metric-card" onClick={onClick}>
<h3>{title}</h3>
<div className="metric-value">{value}</div>
<MiniChart data={chartData} />
</div>
);
});
In a dashboard with 20+ metric cards, this prevented unnecessary chart recalculations when other parts of the dashboard updated. The performance improvement was immediately noticeable to users.
When Should You Use React.memo?
✅ Use React.memo when:
- Component has expensive render logic (complex calculations, large lists)
- Props don't change frequently relative to parent re-renders
- You've identified it as a performance bottleneck using React DevTools Profiler
- Component is used in lists or repeated many times
❌ Don't use React.memo when:
- Component is simple (just renders a few DOM elements)
- Props change on every render anyway
- You haven't measured the performance impact first
- Adding memo would make the code significantly more complex
Advanced: Custom Comparison Function
const UserCard = React.memo(
({ user, lastActive, onEdit }) => {
return (
<div className="user-card">
<h3>{user.name}</h3>
<p>Last active: {lastActive}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
);
},
(prevProps, nextProps) => {
// Only re-render if user data actually changed
// Ignore lastActive changes (too frequent)
return (
prevProps.user.id === nextProps.user.id &&
prevProps.user.name === nextProps.user.name &&
prevProps.onEdit === nextProps.onEdit
);
}
);
I use custom comparison functions when I need fine-grained control over what triggers re-renders. This is particularly useful when some props change frequently but don't affect the visual output.
Best Practices I've Learned
| Practice | Why It Matters |
|---|---|
Always use useCallback for function props |
Function recreation breaks memo optimization |
| Profile before optimizing | React DevTools shows actual impact |
| Memo the leaf components first | Bottom-up optimization is more effective |
| Watch out for object prop mutations | Mutating objects bypasses memo checks |
| Don't memo everything | Comparison overhead can outweigh benefits |
✅ React.memo Quick Reference
| Feature | Description |
|---|---|
| Purpose | Prevents re-renders when props haven't changed |
| Works with | Functional components only |
| Comparison method | Shallow by default, custom function optional |
| Best paired with | useCallback, useMemo for stable props |
| Performance impact | Reduces render cycles, improves UX in heavy components |
| Common gotcha | Object/function props created inline break optimization |
Questions I Get Asked About React.memo
1. What's the difference between React.memo and useMemo?
React.memo wraps entire components to prevent re-renders. useMemo caches expensive calculations inside components. I use memo for component optimization, useMemo for computation optimization.
2. Why isn't memo working with my object props?
Object props are compared by reference. If you create objects inline or don't memoize them, they're "different" every render. Use useMemo to stabilize object props or move object creation outside the render cycle.
3. Should I wrap every component with memo?
No. I only use memo when I've identified actual performance issues. The comparison overhead isn't worth it for simple components or ones that re-render frequently anyway.
4. How is this different from class component PureComponent?
Same concept, different implementation. PureComponent was for class components, React.memo is for functional components. Both do shallow prop comparison to prevent unnecessary renders.
5. Can I use memo with hooks?
Absolutely. React.memo works perfectly with hooks. In fact, hooks like useCallback and useMemo are essential for making memo effective with complex props.