An extensible, low-overhead in-app developer toolbar for React. The package is chrome plus hosting — metrics, flags, overlays and diagnostics are opt-in extensions, each on its own subpath with its own bundle.
$ npm install @nejcm/dev-toolbar# react & react-dom 18 or 19 are required peers# axe-core (ext/a11y) and @testing-library/react (/testing)# are optional peers — install them only if you use those
Each one an opt-in subpath with its own bundle. Import none and the bar hosts only your tools.
// An extension is a plain object.// No registry, no class, no plugin API.const build: DevToolbarExtension = { id: "build-info", label: "Build", align: "end", compact: ({ openPanel }) => ( <button data-dtb-part="trigger" onClick={openPanel}> {import.meta.env.VITE_COMMIT?.slice(0, 7) ?? "dev"} </button> ), panel: () => <BuildDetails />,};
Add them and the extension appears in the ⌘K palette. Contract v2 commands may declare an input schema and resolve a result.
What you return lands in somebody's bug report — and they read the exact text first.
Background work with an AbortSignal that fires on teardown.
Build the object once, at module scope. And treat hidden as does not exist here, not unpainted.
The root entry: every <DevToolbar> prop, the toggle shortcut, the ⋮ menu, the escape hatches and every published type
extension-contract.mdThe object you write, the slot props, start(api), and the two lifecycle rules that bite
kit.mdThe severity vocabulary, storage/poll/style helpers, data-dtb-kind, the React controls and the presentation vocabulary
runtime.mdEvent bus, ring buffers, throttled store, redact() anchored and redactText() scanning
embedding.mdA third-party devtool on the bar: the four-line recipe, the CSS rule, embed(), and one chip for a whole devtools shell
styling.mdTokens, the data-dtb-part list, classNames — and why none of it needs !important
testing.mdrenderWithToolbar, makeExtension(), the fake layout, the mock bus and the Jest caveats
ssr.mdWhy the bar cannot mismatch on hydration, and the one Next.js app-router rule
architecture.mdWhat the shell guarantees, why the boundaries sit where they do, and the known gaps
adr/Decision records: plain-object extensions, light DOM, contract-version policy, per-extension bar presentation
$ bun run verify
format:check → typecheck → lint → knip → build → check:package → test. If it passes locally it passes in CI.
$ bun run playground
A Vite app on :5273 consuming the built dist through file:../.. exactly as a published consumer does.
Bun, not npm. Zero runtime dependencies is a rule. Core never imports runtime/ or ext/. Conventional Commits, enforced — the PR title is the message that lands.