--- name: svelte description: Svelte 5 renderer for json-render that turns JSON specs into Svelte component trees. Use when working with @json-render/svelte, building Svelte UIs from JSON, creating component catalogs, or rendering AI-generated specs. --- # @json-render/svelte Svelte 5 renderer that converts json-render specs into Svelte component trees. ## Quick Start ```svelte ``` ## Creating a Catalog ```typescript import { defineCatalog } from "@json-render/core"; import { schema } from "@json-render/svelte"; import { z } from "zod"; export const catalog = defineCatalog(schema, { components: { Button: { props: z.object({ label: z.string(), variant: z.enum(["primary", "secondary"]).nullable(), }), description: "Clickable button", }, Card: { props: z.object({ title: z.string() }), description: "Card container with title", }, }, }); ``` ## Defining Components Components should accept `BaseComponentProps`: ```typescript interface BaseComponentProps { props: TProps; // Resolved props for this component children?: Snippet; // Child elements (use {@render children()}) emit: (event: string) => void; // Fire a named event bindings?: Record; // Map of prop names to state paths (for $bindState) loading?: boolean; // True while spec is streaming } ``` ```svelte ``` ```svelte

{props.title}

{#if children} {@render children()} {/if}
``` ## Creating a Registry ```typescript import { defineRegistry } from "@json-render/svelte"; import { catalog } from "./catalog"; import Card from "./components/Card.svelte"; import Button from "./components/Button.svelte"; const { registry, handlers, executeAction } = defineRegistry(catalog, { components: { Card, Button, }, actions: { submit: async (params, setState, state) => { // handle action }, }, }); ``` ## Spec Structure (Element Tree) The Svelte schema uses the element tree format: ```json { "root": "card1", "elements": { "card1": { "type": "Card", "props": { "title": "Hello" }, "children": ["btn1"] }, "btn1": { "type": "Button", "props": { "label": "Click me" } } } } ``` ## Visibility Conditions Use `visible` on elements to show/hide based on state: - `{ "$state": "/path" }` - truthy check - `{ "$state": "/path", "eq": value }` - equality check - `{ "$state": "/path", "not": true }` - falsy check - `{ "$and": [cond1, cond2] }` - AND conditions - `{ "$or": [cond1, cond2] }` - OR conditions ## Providers (via JsonUIProvider) `JsonUIProvider` composes all contexts. Individual contexts: | Context | Purpose | | ------------------- | -------------------------------------------------- | | `StateContext` | Share state across components (JSON Pointer paths) | | `ActionContext` | Handle actions dispatched via the event system | | `VisibilityContext` | Enable conditional rendering based on state | | `ValidationContext` | Form field validation | ## Event System Components use `emit` to fire named events. The element's `on` field maps events to action bindings: ```svelte ``` ```json { "type": "Button", "props": { "label": "Submit" }, "on": { "press": { "action": "submit" } } } ``` ## Built-in Actions The `setState` action is handled automatically and updates the state model: ```json { "action": "setState", "actionParams": { "statePath": "/activeTab", "value": "home" } } ``` Other built-in actions: `pushState`, `removeState`, `push`, `pop`. ## Dynamic Props and Two-Way Binding Expression forms resolved before your component receives props: - `{"$state": "/state/key"}` - read from state - `{"$bindState": "/form/email"}` - read + write-back to state - `{"$bindItem": "field"}` - read + write-back for repeat items - `{"$cond": , "$then": , "$else": }` - conditional value For writable bindings inside components, use `getBoundProp`: ```svelte ``` ## Context Helpers Preferred helpers: - `getStateValue(path)` - returns `{ current }` (read/write) - `getBoundProp(() => value, () => bindingPath)` - returns `{ current }` (read/write when bound) - `isVisible(condition)` - returns `{ current }` (boolean) - `getAction(name)` - returns `{ current }` (registered handler) Advanced context access: - `getStateContext()` - `getActionContext()` - `getVisibilityContext()` - `getValidationContext()` - `getOptionalValidationContext()` - `getFieldValidation(ctx, path, config?)` ## Streaming UI Use `createUIStream` for spec streaming: ```svelte {#if stream.spec} {/if} ``` Use `createChatUI` for chat + UI responses: ```typescript const chat = createChatUI({ api: "/api/chat-ui" }); await chat.send("Build a settings panel"); ```