Debugging setup guide
Name the store. Name the action.
A state timeline is only useful when it tells you which store changed and why. Stable store names and specific action labels turn a stream of anonymous updates into a reproduction another developer can follow.
Use a simple convention: name stores by domain,
name actions domain/verb, and never put customer data or
secrets in either label.
Why anonymous updates slow a diagnosis
Zustand's official devtools middleware can label stores,
connections and actions. If an action name is omitted, the DevTools
entry falls back to an anonymous label. That is enough for a quick state
check, but it makes a multi-step bug harder to hand off: several stores
may change, similar clicks may repeat, and the visible symptom may occur
after the first wrong transition.
Name actions in the official middleware
Pass the action label as the third argument to set. A
domain-first label stays readable when timelines from several stores
are inspected together.
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
const useCartStore = create(
devtools(
(set) => ({
items: [],
addItem: (item) =>
set(
(state) => ({ items: [...state.items, item] }),
false,
'cart/addItem'
),
}),
{ name: 'checkout', store: 'cart' }
)
)
The official middleware also accepts an action object when a small, non-sensitive payload makes the entry clearer. Keep labels stable and avoid putting tokens, email addresses or customer content in action metadata.
Give concurrent stores distinct identities
-
Use a stable domain name such as
cart,sessionoreditor. -
If one connection contains several stores, use the middleware's
storeoption to distinguish them. - If several instances exist at once, include a stable instance key that is meaningful in development—not a person's name or production identifier.
-
Do not call everything
store-1. That label stops helping as soon as mount order changes.
Use the same convention in a dedicated trace
Zustand DevTools is a separate, optional workflow. Its bridge registers
each store explicitly and records the same third actionName
argument in the local panel.
import { create } from 'zustand'
import { withDevtoolsBridge } from 'zustand-devtools-bridge'
const useCartStore = create(
withDevtoolsBridge(
(set) => ({
items: [],
addItem: (item) =>
set(
(state) => ({ items: [...state.items, item] }),
false,
'cart/addItem'
),
}),
{
name: 'cart',
enabled: import.meta.env.DEV,
redact: ['user.auth.token', /secret/i],
}
)
)
Enable development instrumentation explicitly for your bundler. The bridge's built-in redaction is a convenience, not a guarantee; add project-specific patterns and review an export before sharing it.
A naming checklist for the next bug
- Write the expected and actual behavior in one sentence each.
- Name every store involved in the reproduction.
- Give each meaningful update a stable
domain/verblabel. - Start from a known state and perform only the required actions.
- Find the earliest unexpected path change, not the final symptom.
- Redact sensitive state before saving or sharing evidence.
Choose the lightest tool that answers the question
The official middleware with Redux DevTools is often enough for named actions and state inspection. A dedicated panel is useful when you want a Zustand-specific multi-store view, path-level comparisons or a redacted offline Trace Session. The two approaches are alternatives for a debugging workflow, not claims that the official tool is inadequate.
Try the naming convention on one real reproduction.
The Store inspector, Timeline and safe time travel are free. Every install also includes three full Trace Session previews.
Install Zustand DevTools