# Json Schema Designer URL: /docs/web/components/json-schema-designer A row-based JSON Schema designer ported from extend-hq's schema-builder. Edit property name, type, description and enum values inline; add children directly from each container; preview the live JSON Schema in the side tab. **Demo:** ```tsx 'use client'; import { useState } from 'react'; import { JsonSchemaDesigner, type JsonSchema } from '@docyrus/ui/components/json-schema-designer'; import { Button } from '@docyrus/ui/primitives/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@docyrus/ui/primitives/ui/dialog'; import { FileJson, Plus } from 'lucide-react'; const SAMPLE_SCHEMA: JsonSchema = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', title: 'User Profile', description: 'A registered platform user.', properties: { id: { type: 'string', format: 'uuid', description: 'Unique identifier' }, email: { type: 'string', format: 'email' }, fullName: { type: 'string', minLength: 1, maxLength: 120 }, age: { type: 'integer', minimum: 0, maximum: 130 }, role: { type: 'string', enum: ['admin', 'editor', 'viewer'], default: 'viewer' }, isActive: { type: 'boolean', default: true }, address: { type: 'object', properties: { street: { type: 'string' }, city: { type: 'string' }, postalCode: { type: 'string', pattern: '^[0-9]{5} ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-json-schema-designer ``` **Dependencies:** - [@dnd-kit/core](https://www.npmjs.com/package/@dnd-kit/core) - [@dnd-kit/sortable](https://www.npmjs.com/package/@dnd-kit/sortable) - [@dnd-kit/utilities](https://www.npmjs.com/package/@dnd-kit/utilities) - [lucide-react](https://www.npmjs.com/package/lucide-react) ## Overview `JsonSchemaDesigner` is a visual editor for [JSON Schema](https://json-schema.org/) (Draft 2020-12 / Draft-07 compatible) based on extend-hq's [schema-builder](https://ui.extend.ai/ui/docs/components/schema-builder) pattern. The UI is a single-pane editable table — there is no separate toolbox, no drag-from-palette, no right-side properties inspector. | Surface | Role | |---------|------| | **Form tab** | An inline editable table. Each property is a row with name, type, description and (when relevant) a nested sub-table for object / array / enum content. `+ Add property` lives at the bottom of every container. | | **JSON tab** | A read-only `
` view of the live JSON Schema output. |
| **Toolbar** | Strict Mode switch, Clear, and an AI Assistant toggle when `renderAiAssistant` is wired. |

It both **renders / edits existing schemas** (pass `value` or `defaultValue`)
and lets you **design new schemas from scratch**.

## Features

- **Row-based editing** — property name, type and description live in the row itself. No popouts.
- **Inline type select** — a dropdown with the scalar JSON types plus `object`, `array` (with a nested type sub-menu) and `enum`.
- **Nested containers** — object properties expand into a sub-table below the row; array `items` (including `array` and `array`) get the same treatment.
- **Enum editor** — string-with-enum + enum-of-arrays both surface a per-value description list.
- **`+ Add property` everywhere** — at the bottom of the root, of each object, and of each array-of-object.
- **Drag-handle reorder** — within a container, properties can be sorted via the handle on the left of each row (powered by `@dnd-kit`).
- **Strict Mode** — a single toolbar switch flips the output between "no required arrays" and "OpenAI Structured Outputs strict-mode rules" (`additionalProperties: false` + every key in `required[]`).
- **Controlled or uncontrolled** — works with `value` + `onChange` or `defaultValue`.
- **Read-only mode** — turns the designer into a schema viewer.

> **What changed from the previous version**
>
> The legacy three-pane drag-drop UI (left Toolbox, center Tree-View, right
> Item Properties) was replaced. Per-property **required** toggle, type-specific
> validation constraints (`minLength`, `pattern`, `minimum`, …), per-property
> `default` and `format`, and the **Undo / redo** history are no longer
> surfaced — they collapsed into the row-based UX. The `DesignerProvider`,
> `useDesignerContext`, `schemaToTree` and `treeToSchema` exports remain for
> any consumer that built a custom panel on top of the legacy state model.

## Usage

```tsx
import { JsonSchemaDesigner } from "@docyrus/ui/components/json-schema-designer";

export function SchemaPage() {
  return (
    
); } ``` The component has a default height of `640px`. Pass a `className` with an explicit height (e.g. `h-[720px]` or `h-full` inside a sized parent) to override it. ### Editing an existing schema Pass `defaultValue` for uncontrolled use — the schema is imported into the tree once on mount: ```tsx ``` ### Controlled mode Pass `value` + `onChange` to drive the schema from your own state. `onChange` fires with the full JSON Schema document after every edit: ```tsx import { useState } from "react"; import { JsonSchemaDesigner, type JsonSchema } from "@docyrus/ui/components/json-schema-designer"; function ControlledDesigner() { const [schema, setSchema] = useState
); } ``` ### Conversion helpers The package exports pure helpers for converting between JSON Schema documents and the designer's internal node tree — useful for persistence or building a custom UI on top of `DesignerProvider` / `useDesignerContext`. ```tsx import { schemaToTree, treeToSchema, treeToJsonString, parseJsonToTree } from "@docyrus/ui/components/json-schema-designer"; const tree = schemaToTree({ type: "object", properties: { id: { type: "string" } } }); const schema = treeToSchema(tree); const json = treeToJsonString(tree); const result = parseJsonToTree('{"type":"object"}'); if ("error" in result) console.error(result.error); else console.log(result.root); ``` ## Supported keywords The new row-based UI is intentionally narrow. It maps onto the subset of JSON Schema that covers ~90% of agent / LLM authoring scenarios: | Group | Keywords surfaced in the UI | |-------|------------------------------| | Identity | `type`, `description` | | Object | `properties`, `required` (derived from Strict Mode), `additionalProperties` (derived from Strict Mode) | | Array | `items` (scalar, object, or enum) | | Enum | `enum` (always under a `string` carrier), `enumDescriptions` per-value description | `title`, `default`, `format`, `minLength`/`maxLength`/`pattern`, `minimum`/`maximum`/`multipleOf`, `minItems`/`maxItems`/`uniqueItems`, and per-property `required` flags are **not** part of the row UI. Pass a controlled `value` containing those keywords and they are silently dropped on the next round-trip (the inbound document parses, the outbound emit only re-emits what the UI tracks). If you need them, drive the schema externally and treat the designer as a read-only authoring surface. ## EditorAgent integration Pair `useApplyJsonSchema` with `renderAiAssistant` to let an LLM author JSON Schema documents. The hook owns the designer's controlled `schema` + `strictMode` state, ships an `applyJsonSchema` tool (which runs strict-mode validation and reports violations for self-correction) AND a `buildEditorContext()` helper that emits the current strict-mode flag plus an authoring hint into the system prompt. ```tsx import { EditorAgent } from '@docyrus/ui/components/editor-agent'; import { JsonSchemaDesigner, useApplyJsonSchema } from '@docyrus/ui/components/json-schema-designer'; export function JsonSchemaPlayground({ client, user, agentId }) { const jsonSchema = useApplyJsonSchema(); const [aiOpen, setAiOpen] = useState(false); return (
); } ``` ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-json-schema-designer ``` **Dependencies:** - [@dnd-kit/core](https://www.npmjs.com/package/@dnd-kit/core) - [@dnd-kit/sortable](https://www.npmjs.com/package/@dnd-kit/sortable) - [@dnd-kit/utilities](https://www.npmjs.com/package/@dnd-kit/utilities) - [lucide-react](https://www.npmjs.com/package/lucide-react) ## Overview `JsonSchemaDesigner` is a visual editor for [JSON Schema](https://json-schema.org/) (Draft 2020-12 / Draft-07 compatible) based on extend-hq's [schema-builder](https://ui.extend.ai/ui/docs/components/schema-builder) pattern. The UI is a single-pane editable table — there is no separate toolbox, no drag-from-palette, no right-side properties inspector. | Surface | Role | |---------|------| | **Form tab** | An inline editable table. Each property is a row with name, type, description and (when relevant) a nested sub-table for object / array / enum content. `+ Add property` lives at the bottom of every container. | | **JSON tab** | A read-only `
` view of the live JSON Schema output. |
| **Toolbar** | Strict Mode switch, Clear, and an AI Assistant toggle when `renderAiAssistant` is wired. |

It both **renders / edits existing schemas** (pass `value` or `defaultValue`)
and lets you **design new schemas from scratch**.

## Features

- **Row-based editing** — property name, type and description live in the row itself. No popouts.
- **Inline type select** — a dropdown with the scalar JSON types plus `object`, `array` (with a nested type sub-menu) and `enum`.
- **Nested containers** — object properties expand into a sub-table below the row; array `items` (including `array` and `array`) get the same treatment.
- **Enum editor** — string-with-enum + enum-of-arrays both surface a per-value description list.
- **`+ Add property` everywhere** — at the bottom of the root, of each object, and of each array-of-object.
- **Drag-handle reorder** — within a container, properties can be sorted via the handle on the left of each row (powered by `@dnd-kit`).
- **Strict Mode** — a single toolbar switch flips the output between "no required arrays" and "OpenAI Structured Outputs strict-mode rules" (`additionalProperties: false` + every key in `required[]`).
- **Controlled or uncontrolled** — works with `value` + `onChange` or `defaultValue`.
- **Read-only mode** — turns the designer into a schema viewer.

> **What changed from the previous version**
>
> The legacy three-pane drag-drop UI (left Toolbox, center Tree-View, right
> Item Properties) was replaced. Per-property **required** toggle, type-specific
> validation constraints (`minLength`, `pattern`, `minimum`, …), per-property
> `default` and `format`, and the **Undo / redo** history are no longer
> surfaced — they collapsed into the row-based UX. The `DesignerProvider`,
> `useDesignerContext`, `schemaToTree` and `treeToSchema` exports remain for
> any consumer that built a custom panel on top of the legacy state model.

## Usage

```tsx
import { JsonSchemaDesigner } from "@docyrus/ui/components/json-schema-designer";

export function SchemaPage() {
  return (
    
); } ``` The component has a default height of `640px`. Pass a `className` with an explicit height (e.g. `h-[720px]` or `h-full` inside a sized parent) to override it. ### Editing an existing schema Pass `defaultValue` for uncontrolled use — the schema is imported into the tree once on mount: ```tsx ``` ### Controlled mode Pass `value` + `onChange` to drive the schema from your own state. `onChange` fires with the full JSON Schema document after every edit: ```tsx import { useState } from "react"; import { JsonSchemaDesigner, type JsonSchema } from "@docyrus/ui/components/json-schema-designer"; function ControlledDesigner() { const [schema, setSchema] = useState
); } ``` ### Conversion helpers The package exports pure helpers for converting between JSON Schema documents and the designer's internal node tree — useful for persistence or building a custom UI on top of `DesignerProvider` / `useDesignerContext`. ```tsx import { schemaToTree, treeToSchema, treeToJsonString, parseJsonToTree } from "@docyrus/ui/components/json-schema-designer"; const tree = schemaToTree({ type: "object", properties: { id: { type: "string" } } }); const schema = treeToSchema(tree); const json = treeToJsonString(tree); const result = parseJsonToTree('{"type":"object"}'); if ("error" in result) console.error(result.error); else console.log(result.root); ``` ## Supported keywords The new row-based UI is intentionally narrow. It maps onto the subset of JSON Schema that covers ~90% of agent / LLM authoring scenarios: | Group | Keywords surfaced in the UI | |-------|------------------------------| | Identity | `type`, `description` | | Object | `properties`, `required` (derived from Strict Mode), `additionalProperties` (derived from Strict Mode) | | Array | `items` (scalar, object, or enum) | | Enum | `enum` (always under a `string` carrier), `enumDescriptions` per-value description | `title`, `default`, `format`, `minLength`/`maxLength`/`pattern`, `minimum`/`maximum`/`multipleOf`, `minItems`/`maxItems`/`uniqueItems`, and per-property `required` flags are **not** part of the row UI. Pass a controlled `value` containing those keywords and they are silently dropped on the next round-trip (the inbound document parses, the outbound emit only re-emits what the UI tracks). If you need them, drive the schema externally and treat the designer as a read-only authoring surface. ## EditorAgent integration Pair `useApplyJsonSchema` with `renderAiAssistant` to let an LLM author JSON Schema documents. The hook owns the designer's controlled `schema` + `strictMode` state, ships an `applyJsonSchema` tool (which runs strict-mode validation and reports violations for self-correction) AND a `buildEditorContext()` helper that emits the current strict-mode flag plus an authoring hint into the system prompt. ```tsx import { EditorAgent } from '@docyrus/ui/components/editor-agent'; import { JsonSchemaDesigner, useApplyJsonSchema } from '@docyrus/ui/components/json-schema-designer'; export function JsonSchemaPlayground({ client, user, agentId }) { const jsonSchema = useApplyJsonSchema(); const [aiOpen, setAiOpen] = useState(false); return ( ( )} /> ); } ``` The matching backend agent must register a tool named `applyJsonSchema` whose input schema mirrors `{ schema: object (required), explanation?: string }`. ## API Reference ### JsonSchemaDesigner | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `JsonSchema` | — | Controlled JSON Schema document. Re-imports the tree when it changes externally. | | `defaultValue` | `JsonSchema` | — | Initial JSON Schema for uncontrolled use. | | `onChange` | `(schema: JsonSchema) => void` | — | Called with the updated JSON Schema after every edit. | | `readOnly` | `boolean` | `false` | Disables all editing — the designer becomes a viewer. | | `defaultStrictMode` | `boolean` | `false` | Initial state of the **Strict Mode** switch (OpenAI Structured Outputs rules). | | `onStrictModeChange` | `(strictMode: boolean) => void` | — | Fires when the user toggles the **Strict Mode** switch. | | `title` | `string` | `'JSON Schema'` | Header title. | | `className` | `string` | — | Extra classes for the root container (overrides the default `640px` height). | | `aiAssistantOpen` | `boolean` | — | Controlled open state for the AI Assistant drawer. When provided the designer stops managing the open state internally — pair with `onAiAssistantOpenChange`. | | `onAiAssistantOpenChange` | `(open: boolean) => void` | — | Fired when the AI Assistant toolbar button toggles the drawer. | | `renderAiAssistant` | `(ctx: IJsonSchemaAiAssistantRenderContext) => ReactNode` | — | Mounts a custom AI Assistant drawer body. When set, the toolbar shows a Bot toggle that opens/closes the drawer; the render fn receives the live schema + strict-mode state. | ### DesignerProvider | Prop | Type | Default | Description | |------|------|---------|-------------| | `children` | `ReactNode` | — | Wrapped subtree. | | `value` | `JsonSchema` | — | Controlled JSON Schema document. | | `defaultValue` | `JsonSchema` | — | Initial JSON Schema for uncontrolled use. | | `onChange` | `(schema: JsonSchema) => void` | — | Fired with the updated JSON Schema after every edit. | | `readOnly` | `boolean` | `false` | Disables editing for all descendants. | | `defaultStrictMode` | `boolean` | `false` | Initial state of the Strict Mode switch. | ## Exports | Export | Description | |--------|-------------| | `JsonSchemaDesigner` | The row-based designer component. | | `useApplyJsonSchema` | Owns the controlled `schema` + `strictMode` state and ships the `applyJsonSchema` client-side tool for ``. Adds strict-mode validation so the agent can iterate to compliance, and exposes `buildEditorContext()` returning a strict-mode-aware system-prompt snippet. See **EditorAgent integration** below. | | `DesignerProvider` | Legacy state provider — kept for consumers that built a custom panel on top of `useDesignerContext`. **Not wired** into the new row-based UI; the designer manages its own internal state instead. | | `useDesignerContext` | Legacy hook to read / mutate the old reducer-backed state inside a `DesignerProvider`. | | `schemaToTree` | Convert a JSON Schema document into the legacy node tree. | | `treeToSchema` | Convert a node tree into a JSON Schema document. | | `treeToJsonString` | Serialize a node tree to a pretty-printed JSON string. | | `parseJsonToTree` | Parse a JSON string into a node tree (or return an `error`). | | `DEFAULT_SCHEMA_DIALECT` | The default `$schema` dialect URI. | | `TOOLBOX_ITEMS` | Legacy palette type entries — no longer surfaced by the row-based UI. | | `TOOLBOX_CATEGORIES` | Legacy palette category names. | ## Type Exports | Type | Description | |------|-------------| | `JsonSchemaDesignerProps` | Props for the `JsonSchemaDesigner` component. | | `DesignerProviderProps` | Props for the `DesignerProvider` component. | | `JsonSchema` | A pragmatic JSON Schema document shape (Draft 2020-12 / Draft-07 compatible). | | `JsonSchemaType` | `'string' \| 'number' \| 'integer' \| 'boolean' \| 'object' \| 'array' \| 'null'`. | | `SchemaNode` | A single node in the designer's internal editable tree. | | `DesignerView` | `'tree' \| 'json'` — the active center-pane tab. | | `ToolboxItemDef` | Legacy palette entry shape — kept for backward compat with custom panels. | | `IJsonSchemaAiAssistantRenderContext` | Argument passed to `renderAiAssistant` — `{ open, width, onClose, schema, strictMode }`. The live schema + strict-mode flag are forwarded so the slot can shape its prompts/tools. | | `IUseApplyJsonSchemaResult` | Return shape of `useApplyJsonSchema` — `{ schema, setSchema, strictMode, setStrictMode, buildEditorContext, tools }`. |