If you use Zustand, you've probably had this moment: the UI is wrong, you know a store changed when it shouldn't have, and now you're scattering console.log calls and refreshing until the bug happens again.
I wanted to see the whole chain instead. So I built Zustand DevTools: a local-first Chrome DevTools panel for registered Zustand stores, action timelines and reproducible Trace Sessions.
The problem
Zustand's appeal is that it's tiny and unopinionated. The trade-off is that a real app can leave you without a clear answer to: what changed, when, and which action caused it?
What I wanted while debugging was deliberately specific:
- a live view of each registered store,
- one timeline across several small stores,
- the exact changed path —
items[1].quantity: 1 → 2— instead of a wall of object output, - and a safe way to inspect an earlier state without corrupting Dates, Maps, Sets or action functions.
What it does
Stores + Timeline are free. Add one wrapper to each store you want to inspect, open Chrome DevTools, and select the Zustand panel. Registered stores appear with stable names and each named update becomes a timeline entry.
[cart] addItem
items[1].quantity: 1 → 2
total: 168 → 297
When the original in-memory snapshot is still available, you can jump to it safely by ID. Entries from before a reload, or from an imported trace, are visibly view-only; the extension never restores a lossy serialized copy into your live app.
Trace Sessions are Pro, with three complete previews included. Start a trace, reproduce the bug and stop. You can then inspect path-level diffs, likely source call-sites, compare any two moments, add bookmarks and notes, and export a redacted session that a teammate can open view-only.
That export is the workflow I wanted most: open step 4 and you can see exactly where the total diverges is much more useful than another paragraph in Slack.
How you wire it up
Install the bridge and wrap the stores you want to inspect:
npm install zustand-devtools-bridge
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,
'addItem'
),
}),
{ name: 'cart' }
)
);
Give each store a stable name and the Stores + Timeline views become accurate. A separate zero-setup Component Hooks (experimental) view can help with a quick look, but it is honestly labelled: React hook values are not guaranteed to be Zustand state.
Two design decisions I care about
- It chains the React DevTools hook instead of replacing it. React DevTools can keep working next to it, whichever extension loads first.
- Application state stays on your machine. There is no analytics, telemetry or developer server. Optional Pro activation sends only the entered licence key, the public product ID and Gumroad's use-count flag to Gumroad after a clear disclosure and consent; it never sends state, traces, URLs or source paths.
The honest part
I build software by directing AI coding agents, and I do not trust their output by default. I own the architecture, naming, test strategy and final review. The current release passes 100 automated tests, including Zustand 4 and 5 type checks, malformed message limits, import validation, licence revocation and the five-profile activation boundary.
After I thought the tool was finished, a dedicated review found three silent failures that the earlier green suite had missed. I fixed them and expanded the regression coverage. A debugging tool that is subtly wrong is worse than no debugging tool at all.
Try it
- Chrome Web Store: https://chromewebstore.google.com/detail/zustand-devtools/flobjdlebndfbpojaepdgnpgflfbelmj
- Bridge on npm: https://www.npmjs.com/package/zustand-devtools-bridge
- Source and setup: https://github.com/KubaOpoczka/zustand-devtools
- Interactive product demo: https://kubaopoczka.github.io/zustand-devtools-site/
- Pro: €9.99 once, three full previews free, up to five Chrome profile/browser activations, and a 30-day refund: https://opoczka.gumroad.com/l/zustand-devtools-pro
If you use Zustand and React, I would genuinely like one kind of feedback: what did you try to inspect that the panel could not explain? That is the shortest path to making it more useful.
P.S. — this is my first DEV post. Corrections, implementation questions and honest product feedback are all welcome.
Top comments (0)