ZU

zustand-state-builder

Implements lightweight, type-safe global state management using Zustand.

Install

mkdir -p .claude/skills/zustand-state-builder && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14199" && unzip -o skill.zip -d .claude/skills/zustand-state-builder && rm skill.zip

Installs to .claude/skills/zustand-state-builder

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Implements lightweight state management using Zustand with TypeScript, persistence, devtools, and modular store patterns. Use when users request "zustand store", "state management", "global state", "zustand setup", or "jotai alternative".
238 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Create basic Zustand stores with state and actions
  • Implement async actions for data fetching and manipulation
  • Integrate DevTools middleware for debugging
  • Add persistence to Zustand stores using localStorage
  • Utilize Immer middleware for immutable updates

How it works

The skill guides the creation of Zustand stores by defining state shapes and actions, integrating TypeScript for type safety, and enabling middleware like DevTools and persistence. It also demonstrates patterns for async actions and immutable updates using Immer.

Inputs & outputs

You give it
State requirements and desired actions
You get back
Zustand store with TypeScript, middleware, and associated hooks

When to use zustand-state-builder

  • Setting up global state in React
  • Persisting state in local storage
  • Adding TypeScript types to Zustand stores
  • Creating modular state slices

About this skill

Zustand State Builder

Build lightweight, scalable state management with Zustand's minimal API.

Core Workflow

  1. Identify state needs: Determine what needs global state
  2. Create store: Define state shape and actions
  3. Add TypeScript types: Full type safety
  4. Enable middleware: Devtools, persist, immer
  5. Split stores: Modular slices for large apps
  6. Connect components: Use hooks to access state

Installation

npm install zustand

# Optional middleware
npm install immer  # For immutable updates

Basic Store

Simple Counter Store

// stores/counter.ts
import { create } from 'zustand';

interface CounterState {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
  incrementBy: (amount: number) => void;
}

export const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
  incrementBy: (amount) => set((state) => ({ count: state.count + amount })),
}));

// Usage in component
function Counter() {
  const { count, increment, decrement } = useCounterStore();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}

Async Actions

// stores/users.ts
import { create } from 'zustand';

interface User {
  id: string;
  name: string;
  email: string;
}

interface UsersState {
  users: User[];
  isLoading: boolean;
  error: string | null;
  fetchUsers: () => Promise<void>;
  addUser: (user: Omit<User, 'id'>) => Promise<void>;
  deleteUser: (id: string) => Promise<void>;
}

export const useUsersStore = create<UsersState>((set, get) => ({
  users: [],
  isLoading: false,
  error: null,

  fetchUsers: async () => {
    set({ isLoading: true, error: null });
    try {
      const response = await fetch('/api/users');
      const users = await response.json();
      set({ users, isLoading: false });
    } catch (error) {
      set({ error: 'Failed to fetch users', isLoading: false });
    }
  },

  addUser: async (userData) => {
    set({ isLoading: true, error: null });
    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(userData),
      });
      const newUser = await response.json();
      set((state) => ({
        users: [...state.users, newUser],
        isLoading: false,
      }));
    } catch (error) {
      set({ error: 'Failed to add user', isLoading: false });
    }
  },

  deleteUser: async (id) => {
    const previousUsers = get().users;
    // Optimistic update
    set((state) => ({
      users: state.users.filter((u) => u.id !== id),
    }));
    try {
      await fetch(`/api/users/${id}`, { method: 'DELETE' });
    } catch (error) {
      // Rollback on error
      set({ users: previousUsers, error: 'Failed to delete user' });
    }
  },
}));

Middleware

DevTools Integration

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';

interface StoreState {
  count: number;
  increment: () => void;
}

export const useStore = create<StoreState>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set(
          (state) => ({ count: state.count + 1 }),
          false,
          'increment' // Action name for devtools
        ),
    }),
    { name: 'CounterStore' } // Store name in devtools
  )
);

Persistence

import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';

interface SettingsState {
  theme: 'light' | 'dark';
  language: string;
  notifications: boolean;
  setTheme: (theme: 'light' | 'dark') => void;
  setLanguage: (language: string) => void;
  toggleNotifications: () => void;
}

export const useSettingsStore = create<SettingsState>()(
  persist(
    (set) => ({
      theme: 'light',
      language: 'en',
      notifications: true,
      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
      toggleNotifications: () =>
        set((state) => ({ notifications: !state.notifications })),
    }),
    {
      name: 'settings-storage', // localStorage key
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({
        // Only persist these fields
        theme: state.theme,
        language: state.language,
        notifications: state.notifications,
      }),
    }
  )
);

Immer Middleware

import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

interface TodosState {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: string) => void;
  updateTodo: (id: string, text: string) => void;
  deleteTodo: (id: string) => void;
}

export const useTodosStore = create<TodosState>()(
  immer((set) => ({
    todos: [],

    addTodo: (text) =>
      set((state) => {
        state.todos.push({
          id: crypto.randomUUID(),
          text,
          completed: false,
        });
      }),

    toggleTodo: (id) =>
      set((state) => {
        const todo = state.todos.find((t) => t.id === id);
        if (todo) {
          todo.completed = !todo.completed;
        }
      }),

    updateTodo: (id, text) =>
      set((state) => {
        const todo = state.todos.find((t) => t.id === id);
        if (todo) {
          todo.text = text;
        }
      }),

    deleteTodo: (id) =>
      set((state) => {
        const index = state.todos.findIndex((t) => t.id === id);
        if (index !== -1) {
          state.todos.splice(index, 1);
        }
      }),
  }))
);

Combined Middleware

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';

export const useStore = create<StoreState>()(
  devtools(
    persist(
      immer((set) => ({
        // ... state and actions
      })),
      { name: 'store' }
    ),
    { name: 'MyStore' }
  )
);

Slices Pattern

Modular Store Architecture

// stores/slices/authSlice.ts
import { StateCreator } from 'zustand';

export interface AuthSlice {
  user: User | null;
  isAuthenticated: boolean;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

export const createAuthSlice: StateCreator<
  AuthSlice & CartSlice, // Combined state type
  [],
  [],
  AuthSlice
> = (set) => ({
  user: null,
  isAuthenticated: false,

  login: async (email, password) => {
    const response = await fetch('/api/auth/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    });
    const user = await response.json();
    set({ user, isAuthenticated: true });
  },

  logout: () => set({ user: null, isAuthenticated: false }),
});
// stores/slices/cartSlice.ts
import { StateCreator } from 'zustand';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

export interface CartSlice {
  items: CartItem[];
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (id: string) => void;
  updateQuantity: (id: string, quantity: number) => void;
  clearCart: () => void;
  totalItems: () => number;
  totalPrice: () => number;
}

export const createCartSlice: StateCreator<
  AuthSlice & CartSlice,
  [],
  [],
  CartSlice
> = (set, get) => ({
  items: [],

  addItem: (item) =>
    set((state) => {
      const existing = state.items.find((i) => i.id === item.id);
      if (existing) {
        return {
          items: state.items.map((i) =>
            i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
          ),
        };
      }
      return { items: [...state.items, { ...item, quantity: 1 }] };
    }),

  removeItem: (id) =>
    set((state) => ({
      items: state.items.filter((i) => i.id !== id),
    })),

  updateQuantity: (id, quantity) =>
    set((state) => ({
      items:
        quantity <= 0
          ? state.items.filter((i) => i.id !== id)
          : state.items.map((i) => (i.id === id ? { ...i, quantity } : i)),
    })),

  clearCart: () => set({ items: [] }),

  totalItems: () => get().items.reduce((sum, i) => sum + i.quantity, 0),

  totalPrice: () =>
    get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
});
// stores/index.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { createAuthSlice, AuthSlice } from './slices/authSlice';
import { createCartSlice, CartSlice } from './slices/cartSlice';

type StoreState = AuthSlice & CartSlice;

export const useStore = create<StoreState>()(
  devtools(
    persist(
      (...args) => ({
        ...createAuthSlice(...args),
        ...createCartSlice(...args),
      }),
      {
        name: 'app-store',
        partialize: (state) => ({
          items: state.items, // Persist cart
          // Don't persist auth (handle with tokens)
        }),
      }
    ),
    { name: 'AppStore' }
  )
);

Selectors

Optimized Selectors

// Avoid re-renders with selectors
function UserName() {
  // Only re-renders when user.name changes
  const userName = useStore((state) => state.user?.name);
  return <span>{userName}</span>;
}

// Multiple values with shallow comparison
import { shallow } from 'zustand/shallow';

function UserInfo() {
  const { name, email } = useStore(
    (state) => ({ name: state.user?.name, email: state.user?.email }),
    shallow
  );
  return (
    <div>
      <p>{name}</p>
      <p>{email}</p>
    </div>
  );
}

// Computed values
function CartSummary() {
  const totalItems = useStore((state) =>
    state.items.reduce((sum, i) => sum + i.quantity, 0)
  );
  const totalPrice = useStore((sta

---

*Content truncated.*

When not to use it

  • When a different state management library is preferred
  • When the application does not require global state management

Limitations

  • Requires `zustand` package installation
  • Requires `immer` package installation for immutable updates

How it compares

This approach provides a structured way to build lightweight and scalable state management using Zustand's minimal API, offering a clear pattern for TypeScript integration and middleware usage compared to manual state management.

Compared to similar skills

zustand-state-builder side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
zustand-state-builder (this skill)06moReviewIntermediate
nextjs-developer3282moNo flagsAdvanced
zustand1132moNo flagsIntermediate
ui-ux-expert-skill919moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

nextjs-developer

zenobi-us

Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.

328531

zustand

lobehub

Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.

113434

ui-ux-expert-skill

fercracix33

Technical workflow for implementing accessible React user interfaces with shadcn/ui, Tailwind CSS, and TanStack Query. Includes 6-phase process with mandatory Style Guide compliance, Context7 best practices consultation, Chrome DevTools validation, and WCAG 2.1 AA accessibility standards. Use after Test Agent, Implementer, and Supabase agents complete their work.

91244

web-artifacts-builder

anthropics

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

49162

accessibility-compliance

wshobson

Implement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.

45132

radix-ui-design-system

sickn33

Build accessible design systems with Radix UI primitives. Headless component customization, theming strategies, and compound component patterns for production-grade UI libraries.

33130

Search skills

Search the agent skills registry