Zustand 5

Modern Zustand 5 patterns for scalable React state management

✨ The solution you've been looking for

Verified
Tested and verified by our team
12565 Stars

Zustand 5 state management patterns. Trigger: When implementing client-side state with Zustand (stores, selectors, persist middleware, slices).

zustand react state-management typescript frontend middleware selectors persistence
Repository

See It In Action

Interactive preview & real-world examples

Live Demo
Skill Demo Animation

AI Conversation Simulator

See how users interact with this skill

User Prompt

Create a Zustand store for managing a counter with increment, decrement, and reset functionality

Skill Processing

Analyzing request...

Agent Response

TypeScript-typed store with proper actions and React component usage example

Quick Start (3 Steps)

Get up and running in minutes

1

Install

claude-code skill install zustand-5

claude-code skill install zustand-5
2

Config

3

First Trigger

@zustand-5 help

Commands

CommandDescriptionRequired Args
@zustand-5 simple-counter-storeCreate a basic Zustand store with actions for a counter componentNone
@zustand-5 persistent-user-settingsImplement a settings store that persists to localStorage using Zustand's persist middlewareNone
@zustand-5 complex-store-architectureDesign a scalable store using slices pattern for large applicationsNone

Typical Use Cases

Simple Counter Store

Create a basic Zustand store with actions for a counter component

Persistent User Settings

Implement a settings store that persists to localStorage using Zustand's persist middleware

Complex Store Architecture

Design a scalable store using slices pattern for large applications

Overview

Basic Store

 1import { create } from "zustand";
 2
 3interface CounterStore {
 4  count: number;
 5  increment: () => void;
 6  decrement: () => void;
 7  reset: () => void;
 8}
 9
10const useCounterStore = create<CounterStore>((set) => ({
11  count: 0,
12  increment: () => set((state) => ({ count: state.count + 1 })),
13  decrement: () => set((state) => ({ count: state.count - 1 })),
14  reset: () => set({ count: 0 }),
15}));
16
17// Usage
18function Counter() {
19  const { count, increment, decrement } = useCounterStore();
20  return (
21    <div>
22      <span>{count}</span>
23      <button onClick={increment}>+</button>
24      <button onClick={decrement}>-</button>
25    </div>
26  );
27}

Persist Middleware

 1import { create } from "zustand";
 2import { persist } from "zustand/middleware";
 3
 4interface SettingsStore {
 5  theme: "light" | "dark";
 6  language: string;
 7  setTheme: (theme: "light" | "dark") => void;
 8  setLanguage: (language: string) => void;
 9}
10
11const useSettingsStore = create<SettingsStore>()(
12  persist(
13    (set) => ({
14      theme: "light",
15      language: "en",
16      setTheme: (theme) => set({ theme }),
17      setLanguage: (language) => set({ language }),
18    }),
19    {
20      name: "settings-storage",  // localStorage key
21    }
22  )
23);

Selectors (Zustand 5)

 1// ✅ Select specific fields to prevent unnecessary re-renders
 2function UserName() {
 3  const name = useUserStore((state) => state.name);
 4  return <span>{name}</span>;
 5}
 6
 7// ✅ For multiple fields, use useShallow
 8import { useShallow } from "zustand/react/shallow";
 9
10function UserInfo() {
11  const { name, email } = useUserStore(
12    useShallow((state) => ({ name: state.name, email: state.email }))
13  );
14  return <div>{name} - {email}</div>;
15}
16
17// ❌ AVOID: Selecting entire store (causes re-render on any change)
18const store = useUserStore();  // Re-renders on ANY state change

Async Actions

 1interface UserStore {
 2  user: User | null;
 3  loading: boolean;
 4  error: string | null;
 5  fetchUser: (id: string) => Promise<void>;
 6}
 7
 8const useUserStore = create<UserStore>((set) => ({
 9  user: null,
10  loading: false,
11  error: null,
12
13  fetchUser: async (id) => {
14    set({ loading: true, error: null });
15    try {
16      const response = await fetch(`/api/users/${id}`);
17      const user = await response.json();
18      set({ user, loading: false });
19    } catch (error) {
20      set({ error: "Failed to fetch user", loading: false });
21    }
22  },
23}));

Slices Pattern

 1// userSlice.ts
 2interface UserSlice {
 3  user: User | null;
 4  setUser: (user: User) => void;
 5  clearUser: () => void;
 6}
 7
 8const createUserSlice = (set): UserSlice => ({
 9  user: null,
10  setUser: (user) => set({ user }),
11  clearUser: () => set({ user: null }),
12});
13
14// cartSlice.ts
15interface CartSlice {
16  items: CartItem[];
17  addItem: (item: CartItem) => void;
18  removeItem: (id: string) => void;
19}
20
21const createCartSlice = (set): CartSlice => ({
22  items: [],
23  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
24  removeItem: (id) => set((state) => ({
25    items: state.items.filter(i => i.id !== id)
26  })),
27});
28
29// store.ts
30type Store = UserSlice & CartSlice;
31
32const useStore = create<Store>()((...args) => ({
33  ...createUserSlice(...args),
34  ...createCartSlice(...args),
35}));

Immer Middleware

 1import { create } from "zustand";
 2import { immer } from "zustand/middleware/immer";
 3
 4interface TodoStore {
 5  todos: Todo[];
 6  addTodo: (text: string) => void;
 7  toggleTodo: (id: string) => void;
 8}
 9
10const useTodoStore = create<TodoStore>()(
11  immer((set) => ({
12    todos: [],
13
14    addTodo: (text) => set((state) => {
15      // Mutate directly with Immer!
16      state.todos.push({ id: crypto.randomUUID(), text, done: false });
17    }),
18
19    toggleTodo: (id) => set((state) => {
20      const todo = state.todos.find(t => t.id === id);
21      if (todo) todo.done = !todo.done;
22    }),
23  }))
24);

DevTools

 1import { create } from "zustand";
 2import { devtools } from "zustand/middleware";
 3
 4const useStore = create<Store>()(
 5  devtools(
 6    (set) => ({
 7      // store definition
 8    }),
 9    { name: "MyStore" }  // Name in Redux DevTools
10  )
11);

Outside React

1// Access store outside components
2const { count, increment } = useCounterStore.getState();
3increment();
4
5// Subscribe to changes
6const unsubscribe = useCounterStore.subscribe(
7  (state) => console.log("Count changed:", state.count)
8);

What Users Are Saying

Real feedback from the community

Environment Matrix

Dependencies

zustand 5.x
React 16.8+
TypeScript 4.5+ (recommended)

Framework Support

React ✓ (recommended) Next.js ✓ Vite ✓ Create React App ✓

Context Window

Token Usage ~3K-8K tokens depending on store complexity

Security & Privacy

Information

Author
prowler-cloud
Updated
2026-01-30
Category
frontend