Zustand is the simplest React state manager — just a hook. But most developers miss the powerful middleware and patterns that make it a full Redux replacement.
The Free APIs You're Missing
1. Slices Pattern — Modular Store
import { create, StateCreator } from "zustand";
interface AuthSlice {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
interface CartSlice {
items: CartItem[];
addItem: (item: CartItem) => void;
total: () => number;
}
const createAuthSlice: StateCreator<AuthSlice & CartSlice, [], [], AuthSlice> = (set) => ({
user: null,
login: async (email, password) => {
const user = await api.login(email, password);
set({ user });
},
logout: () => set({ user: null }),
});
const createCartSlice: StateCreator<AuthSlice & CartSlice, [], [], CartSlice> = (set, get) => ({
items: [],
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
total: () => get().items.reduce((sum, i) => sum + i.price, 0),
});
export const useStore = create<AuthSlice & CartSlice>()((...a) => ({
...createAuthSlice(...a),
...createCartSlice(...a),
}));
2. persist — Automatic LocalStorage Sync
import { persist, createJSONStorage } from "zustand/middleware";
const useSettings = create(
persist(
(set) => ({
theme: "light",
language: "en",
setTheme: (theme: string) => set({ theme }),
}),
{
name: "user-settings",
storage: createJSONStorage(() => sessionStorage),
partialize: (state) => ({ theme: state.theme }),
}
)
);
3. subscribeWithSelector — Fine-Grained Subscriptions
import { subscribeWithSelector } from "zustand/middleware";
const useStore = create(
subscribeWithSelector((set) => ({
count: 0,
name: "default",
increment: () => set((s) => ({ count: s.count + 1 })),
}))
);
// Subscribe to specific field changes
useStore.subscribe(
(state) => state.count,
(count, prevCount) => {
console.log(`Count changed: ${prevCount} -> ${count}`);
if (count > 100) analytics.track("high-count");
}
);
4. immer — Mutable-Style Updates
import { immer } from "zustand/middleware/immer";
const useStore = create(
immer((set) => ({
deeply: { nested: { items: [{ id: 1, done: false }] } },
toggleItem: (id: number) => set((state) => {
const item = state.deeply.nested.items.find((i) => i.id === id);
if (item) item.done = !item.done;
}),
}))
);
5. useStore with Selectors — Prevent Re-Renders
import { useShallow } from "zustand/react/shallow";
// BAD: re-renders on ANY state change
const { name, email } = useStore();
// GOOD: only re-renders when name or email change
const { name, email } = useStore(useShallow((s) => ({ name: s.name, email: s.email })));
// BEST: single value selector
const count = useStore((s) => s.count);
Getting Started
npm install zustand
Need data from any website delivered as clean JSON? I build production web scrapers that handle anti-bot, proxies, and rate limits. 77 scrapers running in production. Email me: Spinov001@gmail.com
Check out my awesome-web-scraping list for the best scraping tools and resources.
Top comments (0)