Skip to content
D
Documentation

Debug with Redux DevTools

how-to
2 min readUpdated

Connect a Zustand store to Redux DevTools and inspect its named state updates.

Use this when you want a development timeline of store updates without adopting Redux. Wrap the store's state creator in devtools, then create the hook with create. Install the Redux DevTools package and the browser extension before running the example:

bash
npm install zustand @redux-devtools/extension

Install the Redux DevTools browser extension as well.

Add the middleware to a React store

For the basic store and component-binding pattern, see Create and bind a React store. The difference here is the devtools wrapper and the third set argument: this complete App.tsx example renders an article status, and each button records a distinct action type in Redux DevTools.

tsx
import { createRoot } from 'react-dom/client'
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

type ArticleStore = {
  status: 'draft' | 'published'
  publish: () => void
  reopen: () => void
}

const useArticleStore = create<ArticleStore>()(
  devtools((set) => ({
    status: 'draft',
    publish: () => set({ status: 'published' }, undefined, 'article/publish'),
    reopen: () => set({ status: 'draft' }, undefined, 'article/reopen'),
  })),
)

function ArticleStatus() {
  const status = useArticleStore((state) => state.status)
  const publish = useArticleStore((state) => state.publish)
  const reopen = useArticleStore((state) => state.reopen)

  return (
    <main>
      <p>Article status: {status}</p>
      <button onClick={publish}>Publish</button>
      <button onClick={reopen}>Reopen</button>
    </main>
  )
}

const container = document.getElementById('root')!
createRoot(container).render(<ArticleStatus />)
The initial draft status appears above the Publish and Reopen buttons.

Open Redux DevTools and select the store to inspect its state and updates. Clicking Publish changes the rendered status to published and records article/publish; clicking Reopen changes it back to draft and records article/reopen. You can inspect either update or use DevTools time travel.

Options to identify the connection

Pass the optional second argument to devtools when you need a distinct connection name or want to control whether the integration runs.

OptionTypeDefaultWhat it does
namestringNot specifiedSets a custom identifier for the Redux DevTools connection.
enabledbooleantrue in development; false in productionEnables or disables the Redux DevTools integration for this store.
anonymousActionTypestringInferred action type, or anonymous if unavailableSets the action type used for mutations without a supplied action name.
storestringNot specifiedSets a custom identifier for this store in Redux DevTools.

For example, add { name: 'CounterStore' } as the second argument to devtools to label the connection. Give updates meaningful action types in set as in the example; without a supplied name, DevTools uses an inferred action type when available and otherwise falls back to anonymous.

Pitfalls

  • devtools requires the @redux-devtools/extension package as well as the browser extension. In production, the integration is disabled by default; set enabled only when you intentionally want a different setting.
  • Redux DevTools displays one store at a time by default. Use its store selector to switch to another store.
  • An update without an explicit action type can appear as anonymous when Zustand cannot infer a name. Add a third set argument, such as 'counter/increment', to make its timeline entry explicit.

Live demo

Try the Zustand live demo to see Zustand running in the browser.

Was this page helpful?