zustand
npx skills add https://github.com/sablier-labs/agent-skills --skill zustand
Agent 安装分布
Skill 文档
Zustand State Management
Your Role
You are an expert in Zustand state management with TypeScript in Next.js projects. You understand Zustand’s lightweight approach to state management, TypeScript integration patterns, middleware composition, and Next.js-specific considerations for Server and Client Components.
Overview
Zustand is a lightweight state management library for React that avoids reducers, context, and boilerplate. It provides a simple API based on hooks, making it ideal for managing global and local state in Next.js applications.
When to use Zustand:
- Global UI state (modals, sidebars, theme preferences)
- Shared application state across multiple components
- Complex state logic that doesn’t belong in individual components
- State that needs to persist across navigation
When to use React state instead:
- Simple local component state
- Form state confined to a single component
- State that doesn’t need to be shared
Quick Start
Create a type-safe store with TypeScript:
"use client";
import { create } from "zustand";
type BearState = {
bears: number;
increase: (by: number) => void;
};
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by }))
}));
Use in components:
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return <h1>{bears} bears</h1>
}
function Controls() {
const increase = useBearStore((state) => state.increase)
return <button onClick={() => increase(1)}>Add bear</button>
}
Project Integration
Store Location
Place stores based on scope:
Shared across all apps (monorepo):
packages/shared/stores/
âââ theme.ts
App-specific stores:
apps/web/stores/
âââ user.ts
Single project:
src/stores/
âââ user.ts
Client Component Requirement
Zustand hooks must be used in Client Components. Add "use client" directive:
"use client"
import { useUserStore } from "@/stores/user"
export function UserButton() {
const name = useUserStore((state) => state.name)
return <button>{name}</button>
}
Server Components
Do not use Zustand in Server Components. For server-side state:
- Use
async/awaitto fetch data directly - Pass data via props to Client Components
- Use URL search params for shareable state
Core Concepts
Store Creation with TypeScript
Always use explicit type annotation with the double-call pattern create<T>()((set) => ...). TypeScript cannot infer
the type automatically because the state generic is invariant.
TypeScript Convention: Use type
Define state using type (not interface) per project conventions:
DO:
type UserState = {
user: User | null;
login: (user: User) => void;
logout: () => void;
};
DON’T:
interface UserState {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
Exception: Module augmentation requires interface:
declare module "zustand" {
interface StoreMutators {
// Must use interface for module augmentation with Zustand
}
}
Selectors
Select only what you need to minimize re-renders. Use useShallow when selecting multiple values to prevent unnecessary
re-renders when objects are shallowly equal.
See ./references/BEGINNER_TYPESCRIPT.md for detailed selector patterns and useShallow usage.
State Updates
Zustand supports partial updates set({ key: value }), functional updates set((state) => ({ ... })), and full
replacement set(state, true).
Common Patterns
Zustand excels at:
- Global UI state – Modals, sidebars, theme preferences
- Async actions – API calls with loading/error states
- Store reset – Return to initial state
- Multiple stores – Domain-specific state separation
See ./references/BEGINNER_TYPESCRIPT.md for complete pattern implementations with code examples.
Middleware
Zustand provides middleware for enhanced functionality:
combine– Automatic type inference by separating state and actionsdevtools– Redux DevTools integration for debuggingpersist– localStorage/sessionStorage persistence
Middleware can be stacked together. Place devtools as the outermost middleware for best type inference.
See ./references/BEGINNER_TYPESCRIPT.md for detailed middleware usage, configuration options, and composition
patterns.
Best Practices
DO
- Use explicit type annotation:
create<State>()((set) => ...) - Select only what you need:
useStore((state) => state.value) - Use
useShallowfor multiple values - Keep stores focused on specific domains
- Use
combinefor automatic type inference - Add
"use client"directive in Client Components
DON’T
- Don’t use Zustand in Server Components
- Don’t select entire state:
const state = useStore() - Don’t use
interfacefor state types (usetypeinstead) - Don’t mutate state directly (use immutable updates)
- Don’t forget the double-call pattern:
create<T>()()
Testing
For testing Zustand stores, consult ./references/TESTING.md for complete Vitest setup, automatic state reset
configuration, and testing patterns.
React 19 Compatibility
Zustand is fully compatible with React 19. No special configuration needed for Next.js 15+ / React 19 setups.
Additional Resources
For detailed implementations and advanced patterns, consult the reference files:
./references/BEGINNER_TYPESCRIPT.md
- Complete pattern implementations with code examples
- Middleware configuration (
combine,devtools,persist) - Type extraction, selectors, async operations
- Multiple stores organization
./references/ADVANCED_TYPESCRIPT.md
- Type inference mechanics and why
create<T>()()is required - Slices pattern for large stores
- Custom middleware authoring
- Vanilla stores (non-React usage)
- Store mutators and advanced TypeScript patterns
./references/TESTING.md
- Testing Zustand stores with Vitest
- Automatic state reset setup
- Component and store testing patterns
- Best practices and troubleshooting
Quick Reference
Create store:
const useStore = create<State>()((set) => ({ ... }))
Use in component:
"use client";
const value = useStore((state) => state.value);
Update state:
set({ key: value }) // Partial
set((state) => ({ ... })) // Functional
set({ key: value }, true) // Replace
Multiple values:
useShallow((state) => ({ a: state.a, b: state.b }));
With middleware:
create<State>()(devtools(persist((set) => ({ ... }))))