import { pageMetadata } from "@/lib/page-metadata" export const metadata = pageMetadata("docs/schemas") # Schemas Schemas define the structure and validation rules for your UI specs. ## What is a Schema? A schema defines the JSON structure that describes your UI. It includes: - **Element structure** — How components are nested and referenced - **Property types** — What props each component accepts - **Data binding syntax** — How to reference dynamic data - **Action format** — How user interactions are defined ## Schema-Agnostic by Design json-render can work with any JSON schema. `@json-render/core` provides the primitives to define catalogs and renderers for any format: - **@json-render/react** — The built-in flat element tree schema - **[A2UI](/docs/a2ui)** — Google's Agent-to-User Interaction protocol - **[Adaptive Cards](/docs/adaptive-cards)** — Microsoft's platform-agnostic UI format - **AG-UI** — CopilotKit's Agent User Interaction Protocol - **OpenAPI/Swagger** — API documentation schemas for dynamic forms - **Custom schemas** — Design your own format tailored to your domain See the [Custom Schema guide](/docs/custom-schema) to learn how to implement support for any schema. ## Built-in Schema `@json-render/react` uses a flat element tree schema with a root key and elements map: ```json { "root": "card-1", "elements": { "card-1": { "type": "Card", "props": { "title": "Dashboard" }, "children": ["text-1", "button-1"] }, "text-1": { "type": "Text", "props": { "content": { "$state": "/user/name" } }, "children": [] }, "button-1": { "type": "Button", "props": { "label": "Click me" }, "children": [] } } } ``` ## Schema Components ### Element Structure In the built-in schema, each element in the elements map has this structure: ```typescript interface Element { type: string; // Component type from catalog props: Record; // Component properties children: string[]; // Array of child element keys visible?: VisibilityCondition; // Conditional display } ``` ### Data Binding Syntax Reference dynamic data using `$state` expressions in props. The value is a JSON Pointer path into the state model: ```json { "type": "Text", "props": { "content": { "$state": "/user/name" }, "count": { "$state": "/items/count" } }, "children": [] } ``` json-render also supports `$item` and `$index` expressions for lists, two-way binding via `$bindState` / `$bindItem`, and conditional props. See [Data Binding](/docs/data-binding) for the full reference. ### Action Format Actions are defined in the catalog and referenced from components. The renderer handles action execution: ```typescript // In your catalog actions: { navigate: { params: z.object({ url: z.string() }), description: 'Navigate to a URL', }, apiCall: { params: z.object({ endpoint: z.string(), method: z.enum(['GET', 'POST', 'PUT', 'DELETE']), }), description: 'Make an API request', }, } ``` ## Custom Schemas `@json-render/core` is schema-agnostic. You can define any JSON structure: ```typescript import { z } from 'zod'; // Define your own element schema const MyElementSchema = z.object({ component: z.string(), settings: z.record(z.unknown()), nested: z.array(z.lazy(() => MyElementSchema)).optional(), }); // Define your own data binding format const BoundValue = z.object({ literal: z.string().optional(), source: z.string().optional(), // e.g., "/users/0/name" }); // Define your own action format const ActionSchema = z.object({ name: z.string(), context: z.record(z.unknown()).optional(), }); ``` ## Schema vs Catalog The schema and catalog work together but serve different purposes: - **Schema** — Defines the JSON structure (how elements are organized) - **Catalog** — Defines available components and their props (what can be used) The schema is the grammar; the catalog is the vocabulary. ## Next Learn about [specs](/docs/specs) — the actual JSON documents that describe your UI.