The Backstory
Why I Used Redux in the First Place
- Predictable state updates with a single store and clear data flow
- DevTools for time-travel debugging and inspecting every action
- Middleware for side effects (e.g. API calls, logging)
- Ecosystem and patterns that many developers already know
What Started to Bother Me
1. Boilerplate
2. Mental Overhead
3. Provider and Setup
4. Not Everything Needs a Global Store
Why I Chose Zustand
What I like about it
- Minimal boilerplate: One file, one function, and you have a store.
- No provider: You create a store and use it. No wrapping the app.
- Simple API: getState(), setState(), and a useStore hook. Easy to read and teach.
- TypeScript-friendly: Typing the store is straightforward.
- Small bundle: ~1–2 kB gzipped, so it doesn’t add much cost.
- Flexible: Use it for global state, or scope it to a part of the tree.
The same UI state in Zustand
Redux vs Zustand: Quick Comparison
When I Still Consider Redux
- The team is large and already standardized on Redux: consistency matters more than my preference.
- We need advanced DevTools: time-travel and action replay can be worth the cost.
- We have complex middleware needs: sagas, strict logging, or very specific side-effect pipelines.
- The product is an existing Redux codebase: rewriting for Zustand isn’t always justified.
Migrating from Redux to Zustand
- Start at the edges: Pick one slice (e.g. UI or feature flags) and replace it with a Zustand store. Keep Redux for the rest.
- Match the shape: Design the Zustand store so components can switch with minimal changes (same field names, similar “selectors” via the hook).
- Split by domain: Use multiple small stores (e.g. useUIStore, useAuthStore) instead of one giant store. It keeps things readable and avoids unnecessary re-renders.
- Use selectors: Zustand’s useStore(selector) only re-renders when the selected slice changes, similar to useSelector. Use it for performance.

