Skip to main content

svelte

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.

Quellinformationen

Repository
vercel-labs/json-render
Letzte Quellaktivität
13. August 2026 um 19:32
Erkannte Sprache von SKILL.md
Englisch
Sterne
18.837
Forks
1.001

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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 <script lang="ts"> import { Renderer, JsonUIProvider } from "@json-render/svelte"; import type { Spec } from "@json-render/svelte"; import Card from "./components/Card.svelte"; import Button from "./components/Button.svelte"; interface Props { spec: Spec | null; } let { spec }: Props = $props(); const registry = { Card, Button }; </script> <JsonUIProvider> <Renderer {spec} {registry} /> </JsonUIProvider> ``` ## 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<TProps>`: ```typescript interface BaseComponentProps<TProps> { props: TProps; // Resolved props for this component children?: Snippet; // Child elements (use {@render children()}) emit: (event: string) => void; // Fire a named event bindings?: Record<string, string>; // Map of prop names to state paths (for $bindState) loading?: boolean; // True while spec is streaming } ``` ```svelte <!-- Button.svelte --> <script lang="ts"> import type { BaseComponentProps } from "@json-render/svelte"; interface Props extends BaseComponentProps<{ label: string; variant?: string }> {} let { props, emit }: Props = $props(); </script> <button class={props.variant} onclick={() => emit("press")}> {props.label} </button> ``` ```svelte <!-- Card.svelte --> <script lang="ts"> import type { Snippet } from "svelte"; import type { BaseComponentProps } from "@json-render/svelte"; interface Props extends BaseComponentProps<{ title: string }> { children?: Snippet; } let { props, children }: Props = $props(); </script> <div class="card"> <h2>{props.title}</h2> {#if children} {@render children()} {/if} </div> ``` ## 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 <!-- Button.svelte --> <script lang="ts"> import type { BaseComponentProps } from "@json-render/svelte"; interface Props extends BaseComponentProps<{ label: string }> {} let { props, emit }: Props = $props(); </script> <button onclick={() => emit("press")}>{props.label}</button> ``` ```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 Nested lists can set `repeat.statePath` to `{ "$item": "field" }` inside an enclosing repeat. 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": <condition>, "$then": <value>, "$else": <value>}` - conditional value For writable bindings inside components, use `getBoundProp`: ```svelte <script lang="ts"> import { getBoundProp } from "@json-render/svelte"; import type { BaseComponentProps } from "@json-render/svelte"; interface Props extends BaseComponentProps<{ value?: string }> {} let { props, bindings }: Props = $props(); let value = getBoundProp<string>( () => props.value, () => bindings?.value, ); </script> <input bind:value={value.current} /> ``` ## 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 <script lang="ts"> import { createUIStream, Renderer } from "@json-render/svelte"; const stream = createUIStream({ api: "/api/generate-ui", onComplete: (spec) => console.log("Done", spec), }); async function generate() { await stream.send("Create a login form"); } </script> <button onclick={generate} disabled={stream.isStreaming}> {stream.isStreaming ? "Generating..." : "Generate UI"} </button> {#if stream.spec} <Renderer spec={stream.spec} {registry} loading={stream.isStreaming} /> {/if} ``` Use `createChatUI` for chat + UI responses: ```typescript const chat = createChatUI({ api: "/api/chat-ui" }); await chat.send("Build a settings panel"); ```
Auf GitHub ansehen