From harness-claude
Debugs Zustand stores with Redux DevTools integration for time-travel debugging, action inspection, and tracing state changes in development.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Debug Zustand stores with Redux DevTools integration for time-travel debugging and action inspection
Implements Zustand middleware for persistence (persist), devtools integration, Immer immutability, and custom store enhancements. Guides composition, best practices, partialize, and migrations.
Creates Zustand stores using the store creator pattern. Provides step-by-step guidance, production-ready code, and best practices for frontend state management tasks.
Creates Zustand stores with TypeScript types, subscribeWithSelector middleware, separated state/actions, individual selectors, and non-React subscriptions. For React state management.
Share bugs, ideas, or general feedback.
Debug Zustand stores with Redux DevTools integration for time-travel debugging and action inspection
devtools middleware from zustand/middleware.name option to identify the store in the DevTools panel (especially when using multiple stores).set() — this shows descriptive action names instead of "anonymous".devtools should be the outermost wrapper.enabled option.// stores/todo-store.ts
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
interface TodoStore {
todos: Array<{ id: string; text: string; done: boolean }>;
addTodo: (text: string) => void;
toggleTodo: (id: string) => void;
removeTodo: (id: string) => void;
}
export const useTodoStore = create<TodoStore>()(
devtools(
(set) => ({
todos: [],
addTodo: (text) =>
set(
(state) => ({
todos: [...state.todos, { id: crypto.randomUUID(), text, done: false }],
}),
false, // replace: false (default merge behavior)
'todos/addTodo' // Action name shown in DevTools
),
toggleTodo: (id) =>
set(
(state) => ({
todos: state.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)),
}),
false,
'todos/toggleTodo'
),
removeTodo: (id) =>
set(
(state) => ({ todos: state.todos.filter((t) => t.id !== id) }),
false,
'todos/removeTodo'
),
}),
{ name: 'TodoStore', enabled: process.env.NODE_ENV === 'development' }
)
);
Middleware stacking order: When combining multiple middlewares, order matters. The outermost middleware wraps everything:
// Correct order: devtools > persist > immer (outermost to innermost)
create<Store>()(
devtools(
persist(
immer((set) => ({
/* ... */
})),
{ name: 'storage-key' }
),
{ name: 'StoreName' }
)
);
Named actions: The third argument to set(state, replace, actionName) appears in the Redux DevTools action log. Without it, every action shows as "anonymous" which makes debugging difficult. Use a slice/action naming convention.
Multiple stores: Each store with devtools appears as a separate instance in the Redux DevTools dropdown. Use distinct name values.
Time-travel debugging: Redux DevTools supports jumping to any previous state. This works with Zustand's devtools middleware — clicking a past action restores the store to that point.
Production safety: Either use enabled: false in production or strip the middleware entirely:
const middlewares = (f: StateCreator<Store>) =>
process.env.NODE_ENV === 'development' ? devtools(f, { name: 'Store' }) : f;
export const useStore = create<Store>()(
middlewares((set) => ({
/* ... */
}))
);
https://zustand.docs.pmnd.rs/middlewares/devtools