Zustand Adapter
Track Zustand store updates in React Dev Debugger for better debugging experience.
Installation
npm install react-dev-debugger zustand
Basic Setup
store.ts
import create from 'zustand';
import { createZustandAdapter } from 'react-dev-debugger/adapters/zustand';
interface BearStore {
bears: number;
increase: () => void;
decrease: () => void;
}
export const useStore = create<BearStore>()((set) => ({
bears: 0,
increase: () => set((state) => ({ bears: state.bears + 1 })),
decrease: () => set((state) => ({ bears: state.bears - 1 })),
}));
// Connect debugger
createZustandAdapter(useStore, {
name: 'Bear Store',
});
Live Playground
Zustand Store
Counter
Count: 0
User State
No user set
Store Setup
import create from 'zustand';
import { createZustandAdapter } from
'react-dev-debugger/adapters/zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({
count: state.count + 1
})),
}));
createZustandAdapter(useStore, {
name: 'My Store',
});State Updates
No updates yet. Interact with the store above.
Zustand Features:
- Track store updates
- Monitor state changes
- View action history
- Simple integration
Usage in Components
BearCounter.tsx
import { useStore } from './store';
export function BearCounter() {
const bears = useStore((state) => state.bears);
const increase = useStore((state) => state.increase);
const decrease = useStore((state) => state.decrease);
return (
<div>
<h1>{bears} bears around here...</h1>
<button onClick={increase}>Add Bear</button>
<button onClick={decrease}>Remove Bear</button>
</div>
);
}
Multiple Stores
stores.ts
import create from 'zustand';
import { createZustandAdapter } from 'react-dev-debugger/adapters/zustand';
// User store
export const useUserStore = create((set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
}));
createZustandAdapter(useUserStore, { name: 'User Store' });
// Cart store
export const useCartStore = create((set) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item],
})),
removeItem: (id) => set((state) => ({
items: state.items.filter(item => item.id !== id),
})),
}));
createZustandAdapter(useCartStore, { name: 'Cart Store' });
With Middleware
Zustand adapter works with all Zustand middleware:
import create from 'zustand';
import { persist } from 'zustand/middleware';
import { createZustandAdapter } from 'react-dev-debugger/adapters/zustand';
interface AppStore {
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark') => void;
}
export const useAppStore = create<AppStore>()(
persist(
(set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
}),
{
name: 'app-storage',
}
)
);
createZustandAdapter(useAppStore, {
name: 'App Store (Persisted)',
});
Complete Example: Todo App
todoStore.ts
import create from 'zustand';
import { createZustandAdapter } from 'react-dev-debugger/adapters/zustand';
interface Todo {
id: number;
text: string;
completed: boolean;
}
interface TodoStore {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
addTodo: (text: string) => void;
toggleTodo: (id: number) => void;
deleteTodo: (id: number) => void;
setFilter: (filter: TodoStore['filter']) => void;
clearCompleted: () => void;
}
export const useTodoStore = create<TodoStore>()((set) => ({
todos: [],
filter: 'all',
addTodo: (text) =>
set((state) => ({
todos: [
...state.todos,
{ id: Date.now(), text, completed: false },
],
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
deleteTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
setFilter: (filter) => set({ filter }),
clearCompleted: () =>
set((state) => ({
todos: state.todos.filter((todo) => !todo.completed),
})),
}));
createZustandAdapter(useTodoStore, {
name: 'Todo Store',
maxSnapshots: 50,
});
TodoApp.tsx
import { useState } from 'react';
import { useTodoStore } from './todoStore';
export function TodoApp() {
const [input, setInput] = useState('');
const { todos, filter, addTodo, toggleTodo, deleteTodo, setFilter, clearCompleted } =
useTodoStore();
const filteredTodos = todos.filter((todo) => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true;
});
const handleAdd = () => {
if (input.trim()) {
addTodo(input);
setInput('');
}
};
return (
<div>
<h1>Zustand Todo List</h1>
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleAdd()}
placeholder="What needs to be done?"
/>
<button onClick={handleAdd}>Add</button>
</div>
<div>
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('completed')}>Completed</button>
</div>
<ul>
{filteredTodos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
{todos.some((t) => t.completed) && (
<button onClick={clearCompleted}>Clear Completed</button>
)}
</div>
);
}
Configuration Options
interface ZustandAdapterOptions {
// Display name in debugger
name?: string;
// Maximum snapshots to keep
maxSnapshots?: number;
// Transform state before displaying
stateTransform?: (state: any) => any;
// Track only specific state slices
trackSlices?: string[];
}
Debugging Tips
Selective State Tracking
Track only parts of your state:
createZustandAdapter(useStore, {
name: 'App Store',
trackSlices: ['user', 'cart'], // Only track these slices
});
State Transformation
Hide sensitive or verbose data:
createZustandAdapter(useStore, {
name: 'User Store',
stateTransform: (state) => ({
user: {
id: state.user?.id,
name: state.user?.name,
// Hide sensitive fields
token: '[REDACTED]',
},
}),
});