A small, fast, and scalable bearbones state-management solution
Listen up, you beautiful disaster. You know how you're drowning in Redux boilerplate, wrestling with Context API's re-render hell, or watching your MobX decorators turn into a fucking archaeology expedition? Yeah, I thought so.
Zustand is here to save your sorry ass. It's a tiny (1kb) state management library that doesn't give a fuck about your opinions, doesn't need providers wrapping your entire app like a goddamn burrito, and won't make you write 50 lines of code just to increment a fucking counter.
🐻 Bear necessities for state management in React.
That's it. That's the whole fucking pitch. No bullshit, no ceremony, just pure state management goodness that actually makes sense.
Redux made you write:
Zustand? Create a store. Done. That's it. No ceremony, no bullshit, no existential crisis at 3 AM wondering why you need three files to manage a boolean.
// Redux: 50+ lines of bullshit for a counter
// Zustand: This beautiful bastard right here
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
// Use it anywhere, no fucking providers needed
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
Context API seemed like a good idea until you realized:
Zustand gives you granular subscriptions. Components only re-render when their specific slice of state changes. Not the whole fucking store. Not everything under the provider. Just what you asked for.
1 kilobyte. ONE. That's smaller than that image you downloaded of "definitely not a crypto scam." Meanwhile, Redux is sitting at 6kb+ (with Redux Toolkit even more), and MobX is having an identity crisis at 16kb.
Remember wrapping your entire app in providers like you're gift-wrapping presents for people you don't like?
// The Redux nightmare
<Provider store={store}>
<ThemeProvider>
<UserProvider>
<LocalizationProvider>
<FeatureFlagProvider>
<App /> {/* Finally! */}
</FeatureFlagProvider>
</LocalizationProvider>
</UserProvider>
</ThemeProvider>
</Provider>
// Zustand
<App /> {/* That's it, you're done */}
Zustand stores are just hooks. Import them, use them, get on with your fucking life.
Redux made you install middleware and write sagas that look like fucking ancient scrolls. Zustand? Just write async functions like a normal human being:
const useStore = create((set) => ({
fishies: {},
fetch: async (pond) => {
const response = await fetch(pond)
set({ fishies: await response.json() })
},
}))
No thunks. No sagas. No Redux-Observable. No sacrificing goats to the async gods. Just fucking async/await like it's 2025 (because it is).
Full TypeScript support without the boilerplate ceremony that makes you want to go back to JavaScript like it's 2015:
interface BearState {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
Redux DevTools support out of the box. Because sometimes you need to time-travel debug your mistakes (we've all been there):
import { devtools } from 'zustand/middleware'
const useStore = create(devtools((set) => ({
// Your store
})))
Want to save state to localStorage? sessionStorage? IndexedDB? Fucking smoke signals? There's middleware for that:
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
const useStore = create(
persist(
(set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
}),
{
name: 'bear-storage', // unique name
},
),
)
Let's talk about what happens when you try to manage localStorage yourself like some kind of masochist:
// The localStorage hell you're currently living in:
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user')
return saved ? JSON.parse(saved) : null
})
useEffect(() => {
localStorage.setItem('user', JSON.stringify(user))
}, [user])
// Oh wait, you have 15 more pieces of state?
// Copy-paste this shit 15 times, you fucking genius.
// Don't forget to handle:
// - Parsing errors (because users WILL fuck with localStorage)
// - Serialization of complex objects
// - Hydration mismatches
// - Race conditions
// - Storage quota exceeded errors
// - Multiple tabs syncing
// And the mental breakdown that comes with all of this
Now watch Zustand handle ALL of this like a fucking boss:
// Zustand: One line and you're done
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
const useStore = create(
persist(
(set) => ({
user: null,
todos: [],
preferences: {},
// Add 100 more fields, I don't give a fuck
updateUser: (user) => set({ user }),
}),
{
name: 'app-storage', // Everything auto-syncs
// It even handles tabs, serialization, errors...
// You know, like a PROFESSIONAL library should
},
),
)
LocalStorage sync across tabs? ✅ Built-in.
Serialization errors? ✅ Handled gracefully.
Partial hydration? ✅ Supported.
Custom storage engines? ✅ IndexedDB, sessionStorage, whatever.
Your sanity? ✅ Actually preserved for once.
Redux: Read 10 articles, watch 5 hours of tutorials, question your sanity, still fuck it up.
Zustand: Read the docs for 10 minutes, start coding, succeed immediately, wonder why you wasted years of your life on Redux.
Then Zustand is STILL perfect because it's so simple it won't hurt you. It's 1kb. Your favicon is probably bigger. Just use it and stop overthinking, you beautiful overthinker.
Then you DEFINITELY need Zustand because Redux will make you want to die and Context will make everything slower than your grandma on dial-up. Zustand scales beautifully without the pain.
npm install zustand
# or if you're a yarn person (no judgment)
yarn add zustand
# or if you're living in the future with pnpm
pnpm add zustand
# or if you're a speed demon with bun
bun add zustand
Bun users: You beautiful speed freaks. Zustand works perfectly with Bun's blazing-fast runtime. Install it, use it, marvel at how fast everything is, and feel superior to npm peasants. You've earned it.
import { create } from 'zustand'
const useTodoStore = create((set) => ({
todos: [],
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: Date.now(), text, done: false }]
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
})),
deleteTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
})),
}))
function TodoList() {
// Select only what you need
const todos = useTodoStore((state) => state.todos)
const toggleTodo = useTodoStore((state) => state.toggleTodo)
const deleteTodo = useTodoStore((state) => state.deleteTodo)
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
)
}
function AddTodo() {
const addTodo = useTodoStore((state) => state.addTodo)
const [text, setText] = useState('')
return (
<form onSubmit={(e) => {
e.preventDefault()
addTodo(text)
setText('')
}}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button>Add</button>
</form>
)
}
That's it. You're done. No more steps. Go build something cool instead of configuring state management for 3 hours.
useShallow from zustand/react/shallow when selecting multiple values to prevent unnecessary re-rendersdevtools + persist + immer = developer happinesssubscribeWithSelector for advanced subscription patternsuseStore.getState() and useStore.setState()Let's be real for a second (just this once).
Redux isn't bad because it's Redux. It's bad because some architect in 2016 decided every fucking app needs enterprise-level state management for a goddamn landing page.
Context isn't bad because it's Context. It's bad because you're using it wrong and re-rendering your entire app tree every time someone clicks a button.
The tool isn't the problem. Using the wrong tool, or using the right tool like a fucking idiot, is the problem.
Zustand is the right tool for 90% of React apps. It's simple, powerful, and doesn't require a PhD in functional programming to understand.
Stop cargo-culting tools. Stop using Redux because "that's what we always used." Stop avoiding state management libraries because you read some article about prop drilling being "good enough."
Use Zustand. It's 2025. State management doesn't need to hurt.
I can't hold your hand forever, you beautiful disaster. Here's where to learn more:
The docs are actually good. Like, shockingly good. They won't make you want to punch your monitor. Revolutionary, I know.