
Zustand 5
Implement Zustand 5 stores, selectors, persist middleware, and slice patterns correctly in React/TypeScript client apps.
Overview
Zustand 5 is an agent skill for the Build phase that implements Zustand 5 client state with stores, selectors, persist middleware, and slice patterns in TypeScript React UIs.
Install
npx skills add https://github.com/prowler-cloud/prowler --skill zustand-5What is this skill?
- Typed `create` stores with actions using functional `set` updates
- Persist middleware for theme, language, and other client settings
- Component usage patterns wiring hooks to increment/decrement and UI controls
- Slice-oriented store composition guidance for larger UIs
- Auto-invoke hint: Using Zustand stores
- Zustand 5 patterns including basic store and persist middleware
- Metadata version 1.0, scope root and ui
Adoption & trust: 543 installs on skills.sh; 14k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are adding client state in a React app and want Zustand 5 done with correct typing, persist, and slice structure instead of ad-hoc global variables.
Who is it for?
Indie builders on React/TypeScript who want lightweight global state with persist and slices without Redux boilerplate.
Skip if: Projects standardized on Redux Toolkit or server-only state where Zustand is not in the stack, or one-off scripts with no UI.
When should I use this skill?
When implementing client-side state with Zustand (stores, selectors, persist middleware, slices) or auto_invoke Using Zustand stores.
What do I get? / Deliverables
You get working `create` stores, persist-backed settings, and component hook wiring aligned with Zustand 5 middleware APIs.
- Typed Zustand store modules with actions
- Persist-configured settings stores and consuming components
Recommended Skills
Journey fit
Client state management is front-end build work, so the canonical placement is build → frontend. The skill documents create(), persist middleware, and component usage patterns—directly supporting UI state during product construction.
How it compares
Use for minimal Zustand 5 recipes instead of copying outdated Zustand 4 persist syntax or defaulting to Redux for small apps.
Common Questions / FAQ
Who is zustand-5 for?
Solo front-end developers using React and TypeScript who need agent-guided Zustand 5 stores, persist middleware, and slice patterns during active UI build.
When should I use zustand-5?
During the build phase on frontend work whenever you implement client-side state with Zustand—stores, selectors, persist middleware, or slices—or when auto_invoke fires on Using Zustand stores.
Is zustand-5 safe to install?
It is documentation and code patterns under Apache-2.0; review the Security Audits panel on this Prism page and treat generated store code like any other dependency you merge.
SKILL.md
READMESKILL.md - Zustand 5
## Basic Store ```typescript import { create } from "zustand"; interface CounterStore { count: number; increment: () => void; decrement: () => void; reset: () => void; } const useCounterStore = create<CounterStore>((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), reset: () => set({ count: 0 }), })); // Usage function Counter() { const { count, increment, decrement } = useCounterStore(); return ( <div> <span>{count}</span> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </div> ); } ``` ## Persist Middleware ```typescript 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<SettingsStore>()( persist( (set) => ({ theme: "light", language: "en", setTheme: (theme) => set({ theme }), setLanguage: (language) => set({ language }), }), { name: "settings-storage", // localStorage key } ) ); ``` ## Selectors (Zustand 5) ```typescript // ✅ Select specific fields to prevent unnecessary re-renders function UserName() { const name = useUserStore((state) => state.name); return <span>{name}</span>; } // ✅ 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 <div>{name} - {email}</div>; } // ❌ AVOID: Selecting entire store (causes re-render on any change) const store = useUserStore(); // Re-renders on ANY state change ``` ## Async Actions ```typescript interface UserStore { user: User | null; loading: boolean; error: string | null; fetchUser: (id: string) => Promise<void>; } const useUserStore = create<UserStore>((set) => ({ user: null, loading: false, error: null, 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 ```typescript // 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<Store>()((...args) => ({ ...createUserSlice(...args), ...createCartSlice(...args), })); ``` ## Immer Middleware ```typescript 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<TodoStore>()( immer((set) => ({ todos: [],