Zustand DevTools

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.

6 minute readpublished 13 Aug 2026Zustand 4 and 5

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 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

  1. Write the expected and actual behavior in one sentence each.
  2. Name every store involved in the reproduction.
  3. Give each meaningful update a stable domain/verb label.
  4. Start from a known state and perform only the required actions.
  5. Find the earliest unexpected path change, not the final symptom.
  6. 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

Primary sources and next reading