If you use Zustand, you've 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, trying to catch the one action that did it. State debugging is detective work — and Zustand, by design, is so minimal that it doesn't give you much to work with.
I wanted to see it instead. So I built Zustand DevTools: a Chrome DevTools panel that records every state change in your app and lets you walk back through them.
The problem
Zustand's whole appeal is that it's tiny and unopinionated — no boilerplate, no context wrappers, no ceremony. The flip side is there's no built-in answer to "what changed, when, and because of which action?" The Redux DevTools middleware exists, but it's Redux-shaped: action-centric, and awkward the moment you have several small stores instead of one big one.
What I actually wanted while debugging was boring and specific:
- a live view of each store's current state,
- a timeline of every change, labelled with the action name,
- the exact path that changed (
items[1].quantity: 1 → 2), not a whole-object diff I have to eyeball, - and the ability to jump back to any point and look around.
What it does
Two parts.
Stores + Timeline (free). Open DevTools, click the Zustand tab, and every registered store shows up live. Every set() becomes a timeline entry with the action name and a path-level diff:
[cart] addItem items[1].quantity: 1 → 2 total: 168 → 297
Click any entry to time-travel to that moment — safely, by ID, rather than by replaying a fragile sequence of actions.
Trace Sessions (Pro). Record while you reproduce a bug, then inspect the recording: path-level diffs, the likely call-site of each change, compare any two entries, and export a redacted session a teammate can import and inspect view-only. That export is the part I use most. "Here's the recording — drag the playhead to step 4 and you'll see where the total goes wrong" beats a paragraph of Slack every single time.
How you wire it up
One wrapper per store you want to inspect:
import { create } from 'zustand';
import { withDevtoolsBridge } from 'zustand-devtools-bridge';
const useCartStore = create(
withDevtoolsBridge(
(set) => ({
items: [],
addItem: (item) =>
set((s) => ({ items: [...s.items, item] }), false, 'addItem'), // named in the log
}),
{ name: 'cart' }
)
);
Register a store once with a stable name and you get an accurate live view of it from then on. (There's also a zero-setup experimental view that reads hooks directly for a quick look — it's clearly labelled, because it can't always tell Zustand state apart from other hooks.)
Two design decisions I care about:
- It chains the DevTools hook instead of clobbering it. The real React DevTools keeps working right next to it, whichever loads first. A debugging tool that breaks your other debugging tools is worse than nothing.
- State never leaves your machine. Data flows from the inspected page to your panel and nowhere else — no server, no analytics, no telemetry. Your app state is often the most sensitive thing on the page; it shouldn't phone home just so you can look at it.
The honest part
I build software by directing AI coding agents, and I don't trust their output by default. The architecture, the naming, the test strategy and the review are mine. There are 96 automated tests across two major framework versions, because a state tool that's subtly wrong is actively dangerous — you'd end up debugging the debugger.
And to be straight: after I'd "finished," I did a dedicated review pass over my own code and found three silent failures a green test suite had missed. I fixed them and kept the diffs. If a tool for catching bugs can't survive its own author looking hard at it, it hasn't earned your trust.
Try it
-
Free: install the Stores + Timeline tools from the Chrome Web Store, add
zustand-devtools-bridgeto your app, and wrap a store. - Pro: unlimited Trace Sessions for a €9.99 one-time purchase — three full previews free, no subscription, up to 5 devices, 30-day refund.
👉 Try it: https://kubaopoczka.github.io/zustand-devtools-site/
If you use Zustand and React, I'd genuinely like to know where it falls short — what you tried to inspect that it couldn't show you. That's the feedback that makes it better.
P.S. — I'm new here: this is my first dev.to post, and honestly still finding my feet sharing work out loud like this. If you've got tips for a newcomer (or thoughts on the tool itself), I'd genuinely love to hear them. 🙏
Top comments (0)