A utility library for creating type-safe and efficient selectors with Zustand. Write concise code with string-based selector keys while maintaining full TypeScript support.
With vanilla Zustand, if you want to select multiple pieces of state, you often end up writing verbose selector functions:
const bears = useStore(state => state.bears);
const fish = useStore(state => state.fish);
const trees = useStore(state => state.trees);
// ...and so onA common workaround is to return an object literal. But this creates a new object on every render, even if the values didn’t change.
const { state1, state2, state3 } = useStore(state => ({
state1: state.state1,
state2: state.state2,
state3: state.state3,
// ...and so on
// ⚠️ This object is freshly created on every render!
}));
// ❌ Zustand compares by reference
// → The selector always returns a "new object"
// → React thinks state changed
// → Component re-renders infinitely✅ Official recommendation: useShallow
From the Zustand docs:
"When you need to subscribe to a computed state from a store, the recommended way is to use a selector. The computed selector will cause a rerender if the output has changed according to Object.is. In this case you might want to use useShallow to avoid a rerender if the computed value is always shallow equal the previous one."
import { useShallow } from 'zustand/shallow';
const { state1, state2 } = useStore(
useShallow(state => ({
state1: state.state1,
state2: state.state2,
})),
);While useShallow solves the re-render problem, the selector code is still:
- 🔁 Repetitive (state1: state.state1)
- 📜 Verbose (lots of boilerplate)
- 🧩 Easy to mistype keys, less TypeScript-friendly
With zustand-selector-helper, you can write:
const { state1, state2, state3 } = useStore({
selectorKeys: ['state1', 'state2', 'state3'],
});Under the hood, this:
- Automatically wraps selectors with Zustand's built-in
useShallow. - Concise, string-based selectors instead of manual functions, removing the need to manually map state.key => state.key. Infers the selected state type from the store → no need to define return types manually.
- Full TypeScript safety with autocompletion & key validation. Ensures only valid keys can be passed (selectorKeys are type-checked).
So you get:
✅ Shallow comparison (no unnecessary re-renders) ✅ Type-safe key selection ✅ Cleaner, more concise syntax
- Type-safe by default – Works seamlessly with React + Zustand. Autocompletion & key validation are fully supported out of the box.
- Performance optimized –
Automatically uses Zustand’s
useShallowfor efficient updates. This avoids unnecessary re-renders by doing shallow comparison of objects.⚠️ Note: Zustand by default compares selector outputs with Object.is (reference equality).useShallowperforms a shallow comparison (top-level properties only). If you need to track changes inside nested objects, you must handle it manually.
- Concise syntax – Write selectors with string keys (selectorKeys: ['key']) instead of verbose selector functions (state => state.key).
- Flexible – Works with any Zustand store structure, whether flat or nested.
# npm
npm install @zuzokim/zustand-selector-helper
# yarn
yarn add @zuzokim/zustand-selector-helper
# pnpm
pnpm add @zuzokim/zustand-selector-helper- Create a typed selector
// store.ts
import { createTypedSelector } from '@zuzokim/zustand-selector-helper';
import { useBearBaseStore } from './bearStore';
// Create a typed selector hook from your Zustand store
export const useBearStore = createTypedSelector(useBearBaseStore);- Use in a React component
// Example.tsx
import { useBearStore } from './store';
function Example() {
// Select only the keys you need
const { bears, fish } = useBearStore({ selectorKeys: ['bears', 'fish'] });
return (
<div>
<p>Bears: {bears}</p>
<p>Fish: {fish}</p>
</div>
);
}- see React Example
pnpm run devContributions are always welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see the LICENSE file for details.
