Welcome students! Today we're diving into Zustand, one of the most popular state management libraries in 2026. Unlike Redux or Context API, itβs famous for being extremely "bearbones," lightweight, and incredibly fast to get started with.
"Zustand" is German for "state." Itβs a small, fast, and scalable state-management solution for React.
- Zero Boilerplate: No need to create Actions, Reducers, or Types (unless you really want to).
- No Providers Needed: You don't have to wrap your entire app in a
<Provider>. - High Performance: It only re-renders the components that actually use the piece of state that changed.
- Hook-Based: It feels exactly like using a custom React hook.
First, install the package using your favorite manager:
pnpm add zustandA "Store" is where your state lives. In Zustand, your store is also a hook!
import { create } from 'zustand';
// Think of 'set' as a way to update the state
const useBearStore = create((set) => ({
bears: 0,
// Actions to update state
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newCount) => set({ bears: newCount }),
}));
export default useBearStore;You can use your store hook anywhere in your application. To keep your app fast, always use Selectors to pick only what you need.
import useBearStore from './store';
function BearCounter() {
// Selector: This component ONLY cares about 'bears'
const bears = useBearStore((state) => state.bears);
return <h1>{bears} bears are in the forest! π»</h1>;
}
function Controls() {
// Selector: This component ONLY cares about the 'increasePopulation' action
const increase = useBearStore((state) => state.increasePopulation);
return <button onClick={increase}>Add a Bear</button>;
}In other libraries, you need middleware (like Thunks) for async tasks. In Zustand, you just write an async function!
const useUserStore = create((set) => ({
users: [],
isLoading: false,
fetchUsers: async () => {
set({ isLoading: true });
try {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
set({ users: data, isLoading: false });
} catch (error) {
console.error("Failed to fetch users", error);
set({ isLoading: false });
}
}
}));What if you want your state (like a shopping cart or user theme) to stay even after the user refreshes the page?
Zustand provides a built-in middleware called persist.
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCartStore = create(
persist(
(set) => ({
items: [],
addItem: (newItem) => set((state) => ({
items: [...state.items, newItem]
})),
}),
{
name: 'shopping-cart', // unique name for localStorage key
}
)
);- Partialize: Only save specific parts of your state.
{ name: 'user-storage', partialize: (state) => ({ token: state.token }), // don't save passwords or large objects! }
- Storage Type: Switch to
sessionStorageif you want state to clear when the tab closes.import { createJSONStorage } from 'zustand/middleware' { name: 'food-storage', storage: createJSONStorage(() => sessionStorage), }
| Feature | Zustand | Redux Toolkit | Context API |
|---|---|---|---|
| Learning Curve | Gentle / Easy | Steep / Complex | Easy |
| Boilerplate | π» Minimal | π§± Heavy | π οΈ Moderate |
| Performance | β‘ High (Selective) | β‘ High (Selective) | π’ Low (Full re-renders) |
| Setup | β±οΈ Seconds | β±οΈ Minutes | β±οΈ Seconds |
| Persistence | πΎ Built-in Middleware | π External (Redux-Persist) | π οΈ Manual Implementation |
- Always use Selectors: Instead of
const state = useStore(), useconst count = useStore(state => state.count). This prevents unnecessary re-renders. - Keep it Simple: Don't over-engineer. Most apps only need a single store for global state.
- Persist Wisely: Only persist what is absolutely necessary. Storing too much in
localStoragecan slow down your app initialization.
- Create a
useCartStorewith an array foritems. - Add an action
addItemthat takes an object and adds it to the list. - Challenge: Use the
persistmiddleware to ensure the items stay in the cart after a page refresh! - Display the total number of items in a navbar component.
Happy Coding! Learn more at zustand.docs.pmnd.rs