| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- import { pageMetadata } from "@/lib/page-metadata"
- export const metadata = pageMetadata("docs/devtools")
- # Devtools
- A drop-in inspector panel for any json-render app. See the spec tree, edit state inline, watch dispatched actions, follow stream patches live, browse your catalog, and pick DOM elements to map them back to spec keys.
- Production-safe: the component tree-shakes to a null render when `NODE_ENV === "production"`.
- ## Install
- Pick the adapter that matches your renderer.
- ### React
- ```bash
- npm install @json-render/devtools @json-render/devtools-react
- ```
- ### Vue
- ```bash
- npm install @json-render/devtools @json-render/devtools-vue
- ```
- ### Svelte
- ```bash
- npm install @json-render/devtools @json-render/devtools-svelte
- ```
- ### Solid
- ```bash
- npm install @json-render/devtools @json-render/devtools-solid
- ```
- ## Quick Start
- Drop `<JsonRenderDevtools />` anywhere inside your existing `<JSONUIProvider>` (or the equivalent provider tree).
- ```tsx
- // React
- import { JsonRenderDevtools } from "@json-render/devtools-react";
- <JSONUIProvider registry={registry} handlers={handlers}>
- <Renderer spec={spec} registry={registry} />
- <JsonRenderDevtools spec={spec} catalog={catalog} />
- </JSONUIProvider>
- ```
- That's it. A floating toggle appears in the bottom-right corner. Click it, or press <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>J</kbd>, to open the drawer.
- ### Chat apps (AI SDK)
- When you're using `@ai-sdk/react`'s `useChat`, pass the `messages` prop so the Stream tab captures spec patches as they arrive:
- ```tsx
- <JsonRenderDevtools
- spec={spec}
- catalog={catalog}
- messages={messages}
- />
- ```
- ## Panels
- <table>
- <thead>
- <tr><th>Tab</th><th>What it shows</th></tr>
- </thead>
- <tbody>
- <tr>
- <td><strong>Spec</strong></td>
- <td>Element tree rooted at <code>spec.root</code>. Expand to walk children. Selecting an element fills a detail pane with its full props, visibility condition, event bindings, watchers, and any issues reported by <code>validateSpec</code>.</td>
- </tr>
- <tr>
- <td><strong>State</strong></td>
- <td>Every leaf path in the state model listed via <code>flattenToPointers</code>. Click a value to edit inline — writes go through <code>store.set</code>, so conditional elements and computed props re-evaluate immediately.</td>
- </tr>
- <tr>
- <td><strong>Actions</strong></td>
- <td>Timeline of dispatched actions: name, params, result or error, duration. Newest first. Expand a row for the full JSON payload.</td>
- </tr>
- <tr>
- <td><strong>Stream</strong></td>
- <td>Patches, text chunks, token usage, and lifecycle markers from the AI generation stream. Grouped by generation.</td>
- </tr>
- <tr>
- <td><strong>Catalog</strong></td>
- <td>Components and actions declared in your catalog with prop chips and type hints.</td>
- </tr>
- <tr>
- <td><strong>Pick</strong></td>
- <td>Click any element in the page to surface its entry in the Spec tab. Works because the renderer transparently tags each element with <code>data-jr-key</code> while devtools is mounted.</td>
- </tr>
- </tbody>
- </table>
- ## Props
- <table>
- <thead>
- <tr><th>Prop</th><th>Type</th><th>Default</th><th>Description</th></tr>
- </thead>
- <tbody>
- <tr>
- <td><code>spec</code></td>
- <td><code>Spec | null</code></td>
- <td><code>null</code></td>
- <td>The spec currently being rendered.</td>
- </tr>
- <tr>
- <td><code>catalog</code></td>
- <td><code>Catalog | null</code></td>
- <td><code>null</code></td>
- <td>Catalog definition — required for the Catalog panel.</td>
- </tr>
- <tr>
- <td><code>messages</code></td>
- <td><code>UIMessage[]</code></td>
- <td><code>undefined</code></td>
- <td>AI SDK <code>useChat</code> messages. Scanned for spec data parts and streamed into the Stream panel.</td>
- </tr>
- <tr>
- <td><code>initialOpen</code></td>
- <td><code>boolean</code></td>
- <td><code>false</code></td>
- <td>Start the drawer open.</td>
- </tr>
- <tr>
- <td><code>position</code></td>
- <td><code>"bottom-right" | "bottom-left" | "right"</code></td>
- <td><code>"bottom-right"</code></td>
- <td>Floating toggle button position.</td>
- </tr>
- <tr>
- <td><code>hotkey</code></td>
- <td><code>string | false</code></td>
- <td><code>"mod+shift+j"</code></td>
- <td>Keyboard shortcut. Use <code>mod</code> for Cmd on macOS / Ctrl elsewhere. Pass <code>false</code> to disable.</td>
- </tr>
- <tr>
- <td><code>bufferSize</code></td>
- <td><code>number</code></td>
- <td><code>500</code></td>
- <td>Max events retained in the ring buffer.</td>
- </tr>
- <tr>
- <td><code>onEvent</code></td>
- <td><code>(evt: DevtoolsEvent) => void</code></td>
- <td><code>undefined</code></td>
- <td>Optional tap — fires for every event as it is recorded. Useful for forwarding to analytics.</td>
- </tr>
- </tbody>
- </table>
- ## Production Safety
- The component renders `null` when `process.env.NODE_ENV === "production"`. Bundlers fold the constant check so the panel's code tree-shakes out of production builds.
- If you want extra certainty, gate the import behind an env check:
- ```tsx
- import dynamic from "next/dynamic";
- const JsonRenderDevtools = dynamic(
- () =>
- import("@json-render/devtools-react").then((m) => ({
- default: m.JsonRenderDevtools,
- })),
- { ssr: false, loading: () => null },
- );
- ```
- ## Advanced
- ### Imperative controls
- Use `useJsonRenderDevtools()` (React adapter only) to open / close the panel or record custom events from anywhere in the app:
- ```tsx
- import { useJsonRenderDevtools } from "@json-render/devtools-react";
- function DebugButton() {
- const devtools = useJsonRenderDevtools();
- return (
- <button onClick={() => devtools?.toggle()}>Toggle devtools</button>
- );
- }
- ```
- ### Server-side stream tap
- Capture stream events before they reach the client. Useful for server logs:
- ```ts
- import { tapJsonRenderStream } from "@json-render/devtools";
- const tapped = tapJsonRenderStream(
- result.toUIMessageStream(),
- serverEventStore,
- );
- writer.merge(pipeJsonRender(tapped));
- ```
- The `@json-render/devtools` core package exports `tapJsonRenderStream` and `tapYamlStream` for this pattern.
|