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/devtools
import { inspect } from "@zoijs/devtools";

inspect(); // a small panel docks in the corner

That'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: S state · C computed · E effect.
  • 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 panel

The 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 observing

How 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.