Zustand (German for "state") provides global state management without the complexity of Redux. Instead of using providers and separate action/reducer files, Zustand allows you to create a store with a single custom hook that defines state and mutation functions through a simple API. There are no dispatch functions or boilerplate reducers—just a plain JavaScript object with methods for updates. This simplicity leads to a shallower learning curve and faster prototyping. Zustand also optimizes component re-renders by preventing updates unless the relevant state changes, making it ideal for mid-sized applications and MVPs.
Installing Zustand
bash
npm install zustand
Creating a Store with Zustand
Stores are created using the create function. You provide a callback that receives set and get functions, and returns your state object along with methods to modify it. Notice how everything lives in one cohesive block—no separate actions or reducers.
jsx
// store/useUserStore.js
import { create } from "zustand";
const useUserStore = create((set, get) => ({
// State
name: "Guest",
isLoggedIn: false,
preferences: { theme: "dark" },
todos: [],
// Actions (methods that update state)
login: (name) => set({ name, isLoggedIn: true }),
logout: () => set({ name: "Guest", isLoggedIn: false }),
toggleTheme: () => set((state) => ({
preferences: {
...state.preferences,
theme: state.preferences.theme === "dark" ? "light" : "dark"
}
})),
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: Date.now(), text, done: false }]
})),
// Using get to access current state inside actions
getTodoCount: () => get().todos.length
}));
export default useUserStore;
Using Zustand in Components
To use the store inside a component, you invoke the custom hook and destructure exactly the pieces you need. Zustand's selector pattern ensures your component only re-renders when those specific fields change—similar to Redux's useSelector but built-in.
jsx
// components/Dashboard.jsx
import useUserStore from "../store/useUserStore";
function Dashboard() {
// Select only what you need—no unnecessary re-renders!
const { name, isLoggedIn, login, logout, toggleTheme, todos } = useUserStore();
// Or select a single field with a selector function
const todoCount = useUserStore((state) => state.todos.length);
return (
<div>
<p>User: {name}</p>
<p>Todo count: {todoCount}</p>
<button onClick={() => login("Megersa")}>Login</button>
<button onClick={logout}>Logout</button>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}