export const metadata = { title: "Specs" } # Specs A spec is a JSON document that describes your UI. ## What is a Spec? A spec (specification) is the actual JSON that describes a UI. It uses components from a [catalog](/docs/catalog) and can optionally follow a [schema](/docs/schemas). Specs can be: - Generated by AI in real-time - Stored in a database - Streamed progressively from a server - Hand-authored as JSON files json-render is schema-agnostic — your specs can follow any JSON structure you choose. ## Example Specs ### Simple Spec A basic spec using the `@json-render/react` schema. Note the flat structure with a `root` key and `elements` map: ```json { "root": "card-1", "elements": { "card-1": { "type": "Card", "props": { "title": "Welcome" }, "children": ["text-1"] }, "text-1": { "type": "Text", "props": { "content": { "$state": "/user/greeting" } }, "children": [] } } } ``` ### Complex Spec A more complex spec with multiple nested elements: ```json { "root": "card-1", "elements": { "card-1": { "type": "Card", "props": { "title": "User Profile", "padding": "md" }, "children": ["row-1", "button-1"] }, "row-1": { "type": "Row", "props": { "gap": "md" }, "children": ["avatar-1", "stack-1"] }, "avatar-1": { "type": "Avatar", "props": { "src": { "$state": "/user/avatar" }, "alt": { "$state": "/user/name" } }, "children": [] }, "stack-1": { "type": "Stack", "props": { "gap": "sm" }, "children": ["name-text", "email-text"] }, "name-text": { "type": "Text", "props": { "content": { "$state": "/user/name" }, "variant": "heading" }, "children": [] }, "email-text": { "type": "Text", "props": { "content": { "$state": "/user/email" }, "variant": "caption" }, "children": [] }, "button-1": { "type": "Button", "props": { "label": "Edit Profile" }, "children": [] } } } ``` ### Block-Level Spec A high-level spec using semantic blocks for page layouts: ```json { "root": "page", "elements": { "page": { "type": "Page", "props": {}, "children": ["header", "hero", "features", "footer"] }, "header": { "type": "Header", "props": { "logo": "/logo.svg", "navItems": ["Products", "Pricing", "Docs"] }, "children": [] }, "hero": { "type": "Hero", "props": { "title": "Build UIs with JSON", "subtitle": "Let AI generate your interfaces", "ctaLabel": "Get Started", "ctaHref": "/docs" }, "children": [] }, "features": { "type": "Features", "props": { "columns": 3 }, "children": ["feature-1", "feature-2", "feature-3"] }, "feature-1": { "type": "Feature", "props": { "icon": "zap", "title": "Fast", "description": "Render UIs in milliseconds" }, "children": [] }, "feature-2": { "type": "Feature", "props": { "icon": "shield", "title": "Secure", "description": "Validate all specs against your catalog" }, "children": [] }, "feature-3": { "type": "Feature", "props": { "icon": "sparkles", "title": "AI-Ready", "description": "Generate prompts from your catalog" }, "children": [] }, "footer": { "type": "Footer", "props": { "copyright": "2025 Acme Inc", "links": ["Privacy", "Terms", "Contact"] }, "children": [] } } } ``` ## Spec Anatomy Specs are schema-agnostic — the JSON structure is entirely up to you. The examples below use the `root` + `elements` flat tree format from the `@json-render/react` schema, which is optimized for AI generation and streaming. ### Root and Elements In the React schema, a spec has a `root` key pointing to the entry element, and an `elements` map containing all elements: ```json { "root": "card-1", "elements": { "card-1": { "type": "Card", "props": { "title": "My Card" }, "children": ["text-1"] }, "text-1": { ... } } } ``` ### Element Structure Each element in the map has a consistent shape: ```json { "type": "ComponentName", "props": { "label": "Hello" }, "children": ["child-1", "child-2"] } ``` - `type` — Component type from your catalog - `props` — Component properties - `children` — Array of child element keys ### Dynamic Data Props can reference data from the state model using `$state` expressions. The value is a JSON Pointer (RFC 6901) path into the state: ```json { "type": "Metric", "props": { "label": "Total Revenue", "value": { "$state": "/metrics/revenue" }, "change": { "$state": "/metrics/revenueChange" } }, "children": [] } ``` See [Data Binding](/docs/data-binding) for the full reference including `$item`, `$index`, repeat, and two-way binding. ### Conditional Visibility Control when elements appear using the `visible` property: ```json { "type": "Alert", "props": { "message": "You have unsaved changes" }, "children": [], "visible": { "$state": "/form/isDirty", "eq": true } } ``` ## Working with Specs ### Validating a Spec Use `validateSpec` from `@json-render/core` to check a spec for structural issues: ```typescript import { validateSpec } from '@json-render/core'; const result = validateSpec(spec); if (!result.valid) { console.error('Invalid spec:', result.issues); } ``` ### Rendering a Spec (React) With `@json-render/react`, wrap the `Renderer` in providers to supply state and visibility: ```tsx import { Renderer, StateProvider, VisibilityProvider } from '@json-render/react'; import { registry } from './registry'; function MyApp({ spec, initialState }) { return ( ); } ``` See the [@json-render/react API reference](/docs/api/react) for full provider and hook documentation. ### Streaming a Spec (React) With `@json-render/react`, use the `useUIStream` hook to stream specs incrementally: ```tsx import { useUIStream } from '@json-render/react'; function GenerativeUI() { const { spec, isStreaming } = useUIStream({ api: '/api/generate', }); return ( ); } ``` See [Streaming](/docs/streaming) for the full SpecStream format and server-side setup. ## Spec Sources Specs can come from various sources: - **AI Generation** — LLMs generate specs based on prompts and catalog - **Database** — Store specs as JSON and load dynamically - **API Response** — Server returns specs based on user/context - **Static Files** — Pre-built specs for known UI patterns ## Next Learn about [catalogs](/docs/catalog) — the vocabulary of components available in your specs.