# 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 }`. |