---
name: zustand-store
description: Zustand state management for React applications with TypeScript. Use for creating stores, managing global state, persistence, and computed values. Triggers on requests for state management, global state, stores, Zustand patterns, or data persistence with localStorage.
---
# Zustand Store Patterns
## Store Structure
```
src/store/
├── todoStore.ts # Domain-specific store
├── uiStore.ts # UI state (modals, sidebars, etc.)
├── index.ts # Barrel exports
```
## Basic Store Pattern
```tsx
// store/todoStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { nanoid } from 'nanoid';
// Entity Type
interface Todo {
id: string;
title: string;
completed: boolean;
createdAt: string;
}
// State Interface
interface TodoState {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
}
// Actions Interface
interface TodoActions {
addTodo: (title: string) => void;
updateTodo: (id: string, updates: Partial<Todo>) => void;
deleteTodo: (id: string) => void;
toggleTodo: (id: string) => void;
setFilter: (filter: TodoState['filter']) => void;
}
// Combined Store Type
type TodoStore = TodoState & TodoActions;
export const useTodoStore = create<TodoStore>()(
persist(
(set) => ({
// Initial State
todos: [],
filter: 'all',
// Actions
addTodo: (title) =>
set((state) => ({
todos: [
...state.todos,
{
id: nanoid(),
title,
completed: false,
createdAt: new Date().toISOString(),
},
],
})),
updateTodo: (id, updates) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, ...updates } : todo
),
})),
deleteTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
setFilter: (filter) => set({ filter }),
}),
{
name: 'todo-storage', // localStorage key
}
)
);
```
## Selectors Pattern
```tsx
// Inline selectors for performance
const todos = useTodoStore((state) => state.todos);
const addTodo = useTodoStore((state) => state.addTodo);
// Computed/derived selectors
const useFilteredTodos = () => {
const todos = useTodoStore((state) => state.todos);
const filter = useTodoStore((state) => state.filter);
return useMemo(() => {
switch (filter) {
case 'active':
return todos.filter((t) => !t.completed);
case 'completed':
return todos.filter((t) => t.completed);
default:
return todos;
}
}, [todos, filter]);
};
// Reusable selector functions
const selectTodoById = (id: string) => (state: TodoStore) =>
state.todos.find((todo) => todo.id === id);
// Usage
const todo = useTodoStore(selectTodoById('abc123'));
```
## Usage in Components
```tsx
// organisms/TodoList/TodoList.tsx
import styles from './TodoList.module.css';
import { useTodoStore } from '../../store';
export const TodoList: FC = () => {
const todos = useTodoStore((state) => state.todos);
const toggleTodo = useTodoStore((state) => state.toggleTodo);
const deleteTodo = useTodoStore((state) => state.deleteTodo);
if (todos.length === 0) {
return <EmptyState />;
}
return (
<ul className={styles.list}>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
=> toggleTodo(todo.id)}
=> deleteTodo(todo.id)}
/>
))}
</ul>
);
};
```
## Store Organization
| Store Type | Purpose | Examples |
|------------|---------|----------|
| Domain Store | Business entities | `todoStore`, `userStore`, `projectStore` |
| UI Store | UI-only state | `modalStore`, `sidebarStore`, `themeStore` |
## Naming Conventions
- **Store file:** `[domain]Store.ts` (camelCase)
- **Hook:** `use[Domain]Store` (e.g., `useTodoStore`)
- **State interface:** `[Domain]State`
- **Actions interface:** `[Domain]Actions`
- **localStorage key:** `[domain]-storage` (kebab-case)
## Barrel Export
```tsx
// store/index.ts
export { useTodoStore } from './todoStore';
export { useUIStore } from './uiStore';
// Re-export types if needed externally
export type { Todo } from './todoStore';
```
## Best Practices
1. **Separate State and Actions** in type definitions for clarity
2. **Use `persist` middleware** for data that should survive page reload
3. **Select only what you need** to prevent unnecessary re-renders
4. **Keep stores focused** - one domain per store
5. **Use `nanoid`** for generating unique IDs
6. **Include timestamps** (`createdAt`, `updatedAt`) for entities
Zustand Store
````markdown
Zustand Store by iButters · 62411d9
npx skillmds@latest add ibutters/zustand-store File contents
---name: zustand-storedescription: ````markdown---````markdown---name: zustand-storedescription: Zustand state management for React applications with TypeScript. Use for creating stores, managing global state, persistence, and computed values. Triggers on requests for state management, global state, stores, Zustand patterns, or data persistence with localStorage.---# Zustand Store Patterns## Store Structure```src/store/├── todoStore.ts # Domain-specific store├── uiStore.ts # UI state (modals, sidebars, etc.)├── index.ts # Barrel exports```## Basic Store Pattern```tsx// store/todoStore.tsimport { create } from 'zustand';import { persist } from 'zustand/middleware';import { nanoid } from 'nanoid';// Entity Typeinterface Todo { id: string; title: string; completed: boolean; createdAt: string;}// State Interfaceinterface TodoState { todos: Todo[]; filter: 'all' | 'active' | 'completed';}// Actions Interfaceinterface TodoActions { addTodo: (title: string) => void; updateTodo: (id: string, updates: Partial<Todo>) => void; deleteTodo: (id: string) => void; toggleTodo: (id: string) => void; setFilter: (filter: TodoState['filter']) => void;}// Combined Store Typetype TodoStore = TodoState & TodoActions;export const useTodoStore = create<TodoStore>()( persist( (set) => ({ // Initial State todos: [], filter: 'all', // Actions addTodo: (title) => set((state) => ({ todos: [ ...state.todos, { id: nanoid(), title, completed: false, createdAt: new Date().toISOString(), }, ], })), updateTodo: (id, updates) => set((state) => ({ todos: state.todos.map((todo) => todo.id === id ? { ...todo, ...updates } : todo ), })), deleteTodo: (id) => set((state) => ({ todos: state.todos.filter((todo) => todo.id !== id), })), toggleTodo: (id) => set((state) => ({ todos: state.todos.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo ), })), setFilter: (filter) => set({ filter }), }), { name: 'todo-storage', // localStorage key } ));```## Selectors Pattern```tsx// Inline selectors for performanceconst todos = useTodoStore((state) => state.todos);const addTodo = useTodoStore((state) => state.addTodo);// Computed/derived selectorsconst useFilteredTodos = () => { const todos = useTodoStore((state) => state.todos); const filter = useTodoStore((state) => state.filter); return useMemo(() => { switch (filter) { case 'active': return todos.filter((t) => !t.completed); case 'completed': return todos.filter((t) => t.completed); default: return todos; } }, [todos, filter]);};// Reusable selector functionsconst selectTodoById = (id: string) => (state: TodoStore) => state.todos.find((todo) => todo.id === id);// Usageconst todo = useTodoStore(selectTodoById('abc123'));```## Usage in Components```tsx// organisms/TodoList/TodoList.tsximport styles from './TodoList.module.css';import { useTodoStore } from '../../store';export const TodoList: FC = () => { const todos = useTodoStore((state) => state.todos); const toggleTodo = useTodoStore((state) => state.toggleTodo); const deleteTodo = useTodoStore((state) => state.deleteTodo); if (todos.length === 0) { return <EmptyState />; } return ( <ul className={styles.list}> {todos.map((todo) => ( <TodoItem key={todo.id} todo={todo} onToggle={() => toggleTodo(todo.id)} onDelete={() => deleteTodo(todo.id)} /> ))} </ul> );};```## Store Organization| Store Type | Purpose | Examples ||------------|---------|----------|| Domain Store | Business entities | `todoStore`, `userStore`, `projectStore` || UI Store | UI-only state | `modalStore`, `sidebarStore`, `themeStore` |## Naming Conventions- **Store file:** `[domain]Store.ts` (camelCase)- **Hook:** `use[Domain]Store` (e.g., `useTodoStore`)- **State interface:** `[Domain]State`- **Actions interface:** `[Domain]Actions`- **localStorage key:** `[domain]-storage` (kebab-case)## Barrel Export```tsx// store/index.tsexport { useTodoStore } from './todoStore';export { useUIStore } from './uiStore';// Re-export types if needed externallyexport type { Todo } from './todoStore';```## Best Practices1. **Separate State and Actions** in type definitions for clarity2. **Use `persist` middleware** for data that should survive page reload3. **Select only what you need** to prevent unnecessary re-renders4. **Keep stores focused** - one domain per store5. **Use `nanoid`** for generating unique IDs6. **Include timestamps** (`createdAt`, `updatedAt`) for entities````
iButters/ClaudeCodePlugins/tree/main/plugins/ui-kit-generator/skills/zustand-store commit 62411d91fe
Frequently asked questions
Run npx skillmds@latest add ibutters/zustand-store in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
````markdown It is listed under Docs & Writing on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
iButters (@ibutters) published this skill. Their other Agent Skills are listed on their SkillMD profile.