Basic Store import { create } from "zustand";
interface CounterStore { count: number; increment: () => void; decrement: () => void; reset: () => void; }
const useCounterStore = create
// Usage function Counter() { const { count, increment, decrement } = useCounterStore(); return (
Persist Middleware import { create } from "zustand"; import { persist } from "zustand/middleware";
interface SettingsStore { theme: "light" | "dark"; language: string; setTheme: (theme: "light" | "dark") => void; setLanguage: (language: string) => void; }
const useSettingsStore = create
Selectors (Zustand 5) // ✅ Select specific fields to prevent unnecessary re-renders function UserName() { const name = useUserStore((state) => state.name); return {name}; }
// ✅ For multiple fields, use useShallow import { useShallow } from "zustand/react/shallow";
function UserInfo() { const { name, email } = useUserStore( useShallow((state) => ({ name: state.name, email: state.email })) ); return
// ❌ AVOID: Selecting entire store (causes re-render on any change) const store = useUserStore(); // Re-renders on ANY state change
Async Actions
interface UserStore {
user: User | null;
loading: boolean;
error: string | null;
fetchUser: (id: string) => Promise
const useUserStore = create
fetchUser: async (id) => {
set({ loading: true, error: null });
try {
const response = await fetch(/api/users/${id});
const user = await response.json();
set({ user, loading: false });
} catch (error) {
set({ error: "Failed to fetch user", loading: false });
}
},
}));
Slices Pattern // userSlice.ts interface UserSlice { user: User | null; setUser: (user: User) => void; clearUser: () => void; }
const createUserSlice = (set): UserSlice => ({ user: null, setUser: (user) => set({ user }), clearUser: () => set({ user: null }), });
// cartSlice.ts interface CartSlice { items: CartItem[]; addItem: (item: CartItem) => void; removeItem: (id: string) => void; }
const createCartSlice = (set): CartSlice => ({ items: [], addItem: (item) => set((state) => ({ items: [...state.items, item] })), removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })), });
// store.ts type Store = UserSlice & CartSlice;
const useStore = create
Immer Middleware import { create } from "zustand"; import { immer } from "zustand/middleware/immer";
interface TodoStore { todos: Todo[]; addTodo: (text: string) => void; toggleTodo: (id: string) => void; }
const useTodoStore = create
addTodo: (text) => set((state) => {
// Mutate directly with Immer!
state.todos.push({ id: crypto.randomUUID(), text, done: false });
}),
toggleTodo: (id) => set((state) => {
const todo = state.todos.find(t => t.id === id);
if (todo) todo.done = !todo.done;
}),
})) );
DevTools import { create } from "zustand"; import { devtools } from "zustand/middleware";
const useStore = create
Outside React // Access store outside components const { count, increment } = useCounterStore.getState(); increment();
// Subscribe to changes const unsubscribe = useCounterStore.subscribe( (state) => console.log("Count changed:", state.count) );