Redux needs 4 files to add one piece of state. Context API re-renders everything. Zustand gives you global state in 3 lines — no providers, no boilerplate, no re-render problems.
What Is Zustand?
Zustand is a small, fast state management library for React. It's 1KB, requires no providers, and only re-renders components that use changed state.
Quick Start
npm install zustand
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
function Counter() {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
return <button onClick={increment}>{count}</button>;
}
That's it. No <Provider>, no reducer, no action types, no dispatch.
Why Zustand Wins
| Feature | Zustand | Redux Toolkit | Context API |
|---|---|---|---|
| Boilerplate | ~5 lines | ~30 lines | ~15 lines |
| Bundle size | 1KB | 11KB | 0 (built-in) |
| Provider needed | No | Yes | Yes |
| Re-render control | Selector-based | Selector-based | Re-renders all |
| Async actions | Just use async/await | Thunks/Sagas | Manual |
| DevTools | Yes (middleware) | Yes | React DevTools |
| Learning curve | 5 minutes | 1-2 hours | 30 minutes |
Real-World Patterns
Async Data Fetching
const useStore = create((set) => ({
users: [],
loading: false,
error: null,
fetchUsers: async () => {
set({ loading: true, error: null });
try {
const res = await fetch('/api/users');
const users = await res.json();
set({ users, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
Persistence (localStorage)
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
theme: 'light',
toggleTheme: () => set((s) => ({
theme: s.theme === 'light' ? 'dark' : 'light'
})),
}),
{ name: 'app-settings' } // localStorage key
)
);
Slices Pattern (Large Apps)
const createAuthSlice = (set) => ({
user: null,
login: async (credentials) => {
const user = await authApi.login(credentials);
set({ user });
},
logout: () => set({ user: null }),
});
const createCartSlice = (set) => ({
items: [],
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
total: () => get().items.reduce((sum, i) => sum + i.price, 0),
});
const useStore = create((...args) => ({
...createAuthSlice(...args),
...createCartSlice(...args),
}));
Key Features
- No providers — works outside React too
- Selector-based re-renders — only what changes
- Middleware — persist, devtools, immer, subscribeWithSelector
- TypeScript — full type inference
- Server-compatible — works with SSR
- Framework agnostic — vanilla version available
Get Started
- Documentation
- GitHub — 50K+ stars
- Recipes
Need real-time data in your React app? My Apify scrapers extract fresh data from any website. Custom solutions: spinov001@gmail.com
Top comments (0)