# Debug store updates

Use [`devtools`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#devtools) to inspect state changes in Redux DevTools without adding Redux or a reducer. Name the connection and each update so you can find the action that changed your state.

## 1. Set up browser debugging

In your React TypeScript project, install Zustand and the extension library:

```bash
npm install zustand @redux-devtools/extension
```

Install the [Redux DevTools browser extension](https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) too. The npm package does not replace the browser extension: the middleware connects through the extension exposed on `window`.

## 2. Name the store and its updates

Wrap the state creator with `devtools` inside [`create`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#create). `devtools` returns a state creator; `create` returns the hook your components use to select state and actions. No provider is needed.

This browser entry point mounts two controls and a bear count. Use it with an HTML element whose id is `root`.

```tsx title="src/main.tsx"
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

type BearStore = {
  bears: number
  addBears: (by: number) => void
  addOne: () => void
}

const useBearStore = create<BearStore>()(
  devtools(
    (set) => ({
      bears: 0,
      addBears: (by) =>
        set(
          (state) => ({ bears: state.bears + by }),
          undefined,
          { type: 'bear/addBears', by },
        ),
      addOne: () => set((state) => ({ bears: state.bears + 1 })),
    }),
    {
      name: 'BearStore',
      anonymousActionType: 'bear/unnamed',
      enabled: true,
    },
  ),
)

function App() {
  const bears = useBearStore((state) => state.bears)
  const addBears = useBearStore((state) => state.addBears)
  const addOne = useBearStore((state) => state.addOne)

  return (
    <main style={{ minHeight: 200 }}>
      <h1>{bears} bears</h1>
      <button onClick={() => addBears(2)}>Add two bears</button>
      <button onClick={addOne}>Add one bear</button>
    </main>
  )
}

const container = document.getElementById('root')!
createRoot(container).render(
  <StrictMode>
    <App />
  </StrictMode>,
)
```

![The initial bear count and two buttons let you trigger the updates to inspect in DevTools.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/6550c8722dc8602a84832e7674494f10.png)

Pass the action label as the third argument to `set`. A string supplies the action type; an object supplies `type` and payload fields. Keep the second argument `undefined` to preserve the default replacement logic.

## 3. Inspect the result

Open Redux DevTools and select the `BearStore` connection. Click **Add two bears**: the page shows `2 bears`, and DevTools records `bear/addBears` with the payload field `by: 2` and the updated `bears` state. Click **Add one bear** next: the page shows `3 bears`, and DevTools records `bear/unnamed` because that update has no explicit action label.

You can also jump to an earlier state in DevTools. The middleware applies that state to the store, so the selected bear count on the page changes with it.

## Options that matter

Pass these options as the second argument to `devtools`.

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `name` | `string` | Not supplied | Names the DevTools connection. Use different names to separate connections. |
| `store` | `string` | Not supplied | Identifies a store within a shared named connection. Its state appears under this key, and its action types get a `store/` prefix. |
| `anonymousActionType` | `string` | Inferred caller name, or `'anonymous'` if unavailable | Supplies the label for updates without an explicit action type. A nonempty value takes precedence over inference. |
| `enabled` | `boolean` | Enabled outside production mode; disabled in production mode | Enables or disables the integration for this store. |

The sample sets `enabled: true` explicitly, so it attempts to connect even in production. For a production build that must not connect, set `enabled: false`. Omit the option to use the middleware's mode-based default. Disabling the integration does not disable state updates or your React controls.

To group multiple stores, give each the same `name` and a distinct `store` identifier. Without `store`, each middleware instance opens its own connection.

## Pitfalls

- If no connection appears, check that the browser extension is available and that `enabled` is not `false`. Without the extension, the store still works, but no debugging connection is created.
- DevTools displays one connection at a time. Use its store selector to choose another connection rather than assuming the other store is missing.
- Use explicit third-argument labels when you need stable action names. Updates without them use `anonymousActionType`, caller-name inference, or the `'anonymous'` fallback.
- For middleware composition and slice typing, follow [Type stores and middleware](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/type-stores-and-middleware).

## Live demo and related pages

Try the [live Zustand counter demo](https://zustand-demo.pmnd.rs/) for the hook-and-action interaction. The sample above adds named DevTools updates to that pattern.

- [Update state with actions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/update-state-with-actions)
- [Split a store into slices](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/split-a-store-into-slices)
- [Store scope and lifecycle](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/store-scope-and-lifecycle)
