DevTools — @zoijs/devtools
See the thing Zoijs is built on. @zoijs/devtools is a reactive-graph inspector: attach it and you can watch a single state.set(...) wake exactly one effect and update exactly one DOM node — no Virtual DOM, no diff, nothing else touched. There's no time-travel and no render replay, because Zoijs has no re-render to replay. The panel shows the live graph as it actually is.
npm i -D @zoijs/devtoolsimport { inspect } from "@zoijs/devtools";
inspect(); // a small panel docks in the cornerThat's the whole setup. Change something in your app and watch a node flash; hover a binding and the one DOM node it updates is outlined on the page.
What the panel shows#
- Every node, colour-coded:
Sstate ·Ccomputed ·Eeffect. - The current value of each state and computed, plus a run / write counter.
- A live flash on the node that just ran or changed — the proof that one update touches one place, not the whole tree.
- The DOM target of each binding effect (
<button> text,<a href=…>). Hover a row to outline that element.
inspect({ corner: "bottom-left" }); // dock on the left instead
const panel = inspect();
panel.close(); // detach and remove the panelThe headless model#
inspect() is a thin panel over createInspector(). Reach for the model directly to build your own UI, drive a browser extension, or assert on the graph in a test:
import { createInspector } from "@zoijs/devtools";
const ins = createInspector().attach();
ins.subscribe((change) => {
// change.type: "create" | "run" | "write" | "dispose"
});
ins.model.stats(); // { states, computeds, effects, runs, writes, … }
ins.model.nodes(); // every node, in creation order
ins.model.observers(rec); // who depends on this node
ins.model.sources(rec); // what this node reads
ins.detach(); // stop observingHow it stays honest#
The inspector rides a dev-only, read-only hook in the core (@zoijs/core/devtools, RFC 0005):
- Dev-only. The hook is a no-op under
configure({ dev: false }). Ship in production mode and nothing attaches, nothing is exposed. - Read-only. It only observes the graph — it never mutates a node or a value.
- Free on the hot path. The hook never instruments reads (
.get()); it reconstructs edges from each node on demand. Watching costs nothing per read. - Non-perturbing. The panel is built from plain DOM, not Zoijs reactivity, so it doesn't add nodes to the graph it's inspecting.
- Zero dependencies, like the rest of Zoijs — it depends only on
@zoijs/core.
Why no time-travel?#
Frameworks with a Virtual DOM can replay renders because rendering is their unit of work. Zoijs doesn't re-render: a value changes, the exact effects that read it run, and they update their nodes in place. There's no frame to step through — so instead of a timeline of renders, the panel gives you the live wiring and shows it light up. That's the model, made visible.
Next: Server Rendering.