From scaffolding
Zustand-based state management standards. Use when creating stores, managing global state, or choosing between local and global state.
npx claudepluginhub komluk/scaffolding --plugin scaffoldingThis skill uses the workspace's default tool permissions.
Zustand-based state management standards and best practices.
Integrates Mem0 persistent memory for Claude Code tasks using MCP tools. Retrieves relevant memories on new tasks, stores learnings like decisions and strategies, captures session states.
Creates web-based slidedecks for developers using Slidev with Markdown, Vue components, code highlighting, animations, interactive demos, and presenter notes. Use for technical presentations, conference talks, code walkthroughs, and workshops.
Zustand-based state management standards and best practices.
| Category | Solution | Use When |
|---|---|---|
| Local UI | useState | Single component, simple state |
| Shared UI | Zustand store | Multiple components need same data |
| Server data | React Query / SWR | API data with caching needs |
| Form state | useState / form library | Form inputs, validation |
| URL state | useSearchParams | Filter/sort params, shareable URLs |
| Element | Requirement |
|---|---|
| Interface | Define typed state + actions interface |
| State | Group related state together |
| Actions | Define all mutations as functions |
| Naming | use[Domain]Store convention |
| Principle | Description |
|---|---|
| Single responsibility | One store per domain (projects, UI, settings) |
| Flat structure | Avoid deeply nested state |
| Immutable updates | Always return new objects |
| Colocated actions | Keep actions inside store definition |
| Rule | Reason |
|---|---|
| Select minimal data | Reduces re-renders |
Use shallow for objects | Prevents unnecessary updates |
| Memoize derived data | Use useMemo for computed values |
| Avoid selecting entire store | Causes re-render on any change |
| Pattern | Use Case |
|---|---|
| Single value | (state) => state.count |
| Multiple values | (state) => ({ a: state.a, b: state.b }), shallow |
| Derived value | useMemo outside store |
| Middleware | Purpose | When to Use |
|---|---|---|
persist | LocalStorage persistence | User preferences, settings |
devtools | Redux DevTools integration | Development debugging |
immer | Immutable updates | Complex nested state |
subscribeWithSelector | Granular subscriptions | Performance optimization |
src/stores/
├── index.ts # Re-exports all stores
├── projectStore.ts # Domain-specific store
├── uiStore.ts # UI state (modals, panels)
└── settingsStore.ts # User preferences
| Store Type | Contains |
|---|---|
| Domain store | Business entities, selections |
| UI store | Modal states, panel visibility, loading |
| Settings store | User preferences, persisted config |
shallow equality for object selectionssubscribeWithSelector for side effectsshallow| Anti-Pattern | Problem | Solution |
|---|---|---|
| Giant store | Hard to maintain, performance issues | Split by domain |
| Computed in store | Stale data, extra complexity | Use useMemo |
| Prop drilling | Bypasses store benefits | Use store directly |
| Store in useState | Loses reactivity | Use store hook |
| No TypeScript | Runtime errors | Define interfaces |
| Scenario | Recommended Solution |
|---|---|
| Form input state | useState |
| Modal open/close | useState or UI store |
| Selected item (shared) | Domain store |
| User preferences | Persisted store |
| API response data | Store + service function |
| Computed/derived data | useMemo from store values |
| Global loading state | UI store |
src/stores/)| Store | Persist | Purpose |
|---|---|---|
workspaceStore.ts | Yes (scaffolding-workspaces) | Projects/workspaces, active selection |
taskStore.ts | No | Task CRUD, SSE updates, agent statuses |
authStore.ts | Yes (auth-storage) | GitHub OAuth state, user profile |
Every store defines type XxxState and type XxxActions, then exports the combined type:
type WorkspaceState = {
workspaces: Workspace[];
activeWorkspace: Workspace | null;
};
type WorkspaceActions = {
addWorkspace: (pathOrWorkspace: string | Workspace) => void;
removeWorkspace: (path: string) => Promise<void>;
setActiveWorkspace: (path: string) => void;
loadFromApi: () => Promise<void>;
};
export type WorkspaceStore = WorkspaceState & WorkspaceActions;
partializePersisted stores use partialize to exclude actions and transient state from storage:
export const useWorkspaceStore = create<WorkspaceStore>()(
persist(
(set, get) => ({ /* state + actions */ }),
{
name: 'scaffolding-workspaces',
partialize: (state) => ({
workspaces: state.workspaces,
activeWorkspace: state.activeWorkspace,
}),
}
)
);
useShallow (src/hooks/)Stores are consumed through hook wrappers that use useShallow to prevent re-renders:
import { useWorkspaceStore } from '../stores/workspaceStore';
import { useShallow } from 'zustand/shallow';
export function useWorkspace() {
return useWorkspaceStore(
useShallow((state) => ({
workspaces: state.workspaces,
activeWorkspace: state.activeWorkspace,
addWorkspace: state.addWorkspace,
// ... selected fields only
}))
);
}
loadFromApi)Stores hydrate from localStorage first, then enrich from API:
persist middleware loads cached state from localStorage on mountloadFromApi() fetches from backend, merges fields (id, github_url, role) into existing entriesNon-persisted stores receive real-time updates from Server-Sent Events:
addTask(task) - deduplicates before prepending to listupdateTask(task) - replaces full task object in list and currentTaskupdateTaskPartial(id, updates) - merges partial fields from SSE completion eventsupdateAgentStatus(taskId, status) - tracks per-agent progress within a taskStores use extractErrorMessage(err, fallbackMsg) utility and store errors in state:
catch (err) {
const message = extractErrorMessage(err, 'Failed to fetch tasks');
set({ error: message, isLoading: false });
}
For expected errors (e.g., 409 conflict on cancel), check AxiosError status before setting error state.