Zustand
Zustand
Zustand (German for “state”) is a small, fast, and scalable bearbones state-management solution using simplified flux principles.
It has rapidly become the preferred choice for global Client State in modern React applications, often replacing Redux due to its extreme simplicity and lack of boilerplate.
Why Zustand?
- No Boilerplate: You don’t need Actions, Reducers, or Dispatchers. You just define your state and functions to update it.
- No Providers: Unlike Context API or Redux, Zustand does not require you to wrap your application in a
<Provider>component. This prevents “Provider Hell” in your component tree. - Renders Optimization: Zustand hooks only trigger a re-render if the specific piece of state you are selecting changes, preventing unnecessary renders.
- Hooks-First API: The API is built entirely around React Hooks, making it feel very native to modern React development.
Creating a Zustand Store
You create a store using the create function. You pass it a callback that receives a set function (used to update the state) and a get function (used to read the current state).
import { create } from 'zustand';
// Create a custom hook that holds our global state
const useBearStore = create((set) => ({
// 1. State values
bears: 0,
// 2. Actions (functions that update state)
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}));
Using the Store in Components
To use the store, you simply call the hook inside your component and pass it a “selector” function to grab only the specific data you need.
function BearCounter() {
// Select ONLY the 'bears' count.
// This component will only re-render when 'bears' changes.
const bears = useBearStore((state) => state.bears);
return <h1>{bears} around here ...</h1>;
}
function Controls() {
// Select ONLY the 'increasePopulation' function.
// This component will NEVER re-render, even when the bear count goes up!
const increasePopulation = useBearStore((state) => state.increasePopulation);
return <button onClick={increasePopulation}>one up</button>;
}
function App() {
return (
<>
<BearCounter />
<Controls />
</>
);
}
Async Actions
Because Zustand doesn’t enforce strict reducers, handling asynchronous logic is incredibly simple. You just write an async function in your store and call set when the promise resolves. No need for middleware like Redux Thunk.
const useUserStore = create((set) => ({
user: null,
loading: false,
fetchUser: async (id) => {
set({ loading: true }); // Update state synchronously
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
set({ user: data, loading: false }); // Update state when async finishes
}
}));
Zustand vs Context API
While React Context is great for injecting dependencies (like a Theme or Router), it is terrible for high-frequency state updates. When a Context value changes, every single component that consumes that Context re-renders, regardless of which part of the value they actually care about.
Zustand’s selector pattern solves this completely, making it far superior for complex global state.
Interview Questions
Q: How does Zustand compare to React’s native Context API for global state?
A: Zustand is significantly more performant for global state. When a value in a React Context changes, every component consuming that context re-renders. Zustand allows components to selectively subscribe to specific “slices” of the store, ensuring a component only re-renders when the exact slice of data it cares about changes.