Fine-grained selectors save renders in Zustand
Why useMemo will not save you
December 5, 2025
While building a new Zustand store for a new project at work, I came across an interesting example to demonstrate the idea that the way you select state impacts performance. Inspired by Romaine's post on reactivity, I created a demo to show the difference between broad and narrow selectors.
The Problem
Consider a simple saved items feature with heart buttons. A simple approach to detect isSaved state would be to select the entire savedItems array and and then use useMemo to memoize the find operation.
// Button component
function HeartButton({ productId }) {
// Broad selector - selects the entire savedItems array
const savedItems = useZustandStore((state) => state.savedItems)
const isSaved = useMemo(
() => savedItems.some((item) => item.productId === productId),
[savedItems, productId],
)
}
Another approach would be to narrow down the selector by running the find operation inside the selector function.
// Button component
function HeartButton({ productId }) {
// Narrow selector - selects only the boolean state
// for the current product
const isSaved = useZustandStore((state) =>
state.savedItems.some((item) => item.productId === productId),
)
}
Example
Here's the two React components above built and rendered within an app, like so:
// App component
function App() {
const productIds = [1, 2, 3, 4, 5, 6, 7, 8]
return (
<div className="container">
{productIds.map((id) => (
<HeartButton key={id} productId={id} />
))}
</div>
)
}
Watch the yellow flashes - they show re-renders.
Broad Selector
Broad Selector
Uses broad selector with useMemo. Watch ALL buttons flash when you click any one.
Narrow Selector
Narrow Selector
Uses narrow selector. Only the affected component re-renders.
The Numbers
From React Profiler measurements with 8 buttons:
- Broad selector: ~0.3ms per interaction × 8 renders = 2.4ms wasted
- Narrow selector: ~0.3ms per interaction × 1 render = 0.3ms total
And this scales linearly with the number of buttons on your page!