Svelte

作者 vercel-labsfc2a696a50a3無授權條款18K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫昨天更新

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.

AI 產生的概覽

將 json-render JSON 規格渲染為 Svelte 5 元件樹,涵蓋目錄、登錄檔、動作與串流 UI。

功能
此技能說明 @json-render/svelte,這是一個把 json-render 規格轉換成 Svelte 元件樹的 Svelte 5 渲染器。內容涵蓋以 Zod 結構定義元件目錄、建立包含元件與動作的登錄檔,以及透過 Renderer 與 JsonUIProvider 渲染規格。它也說明元素樹規格格式、可見性條件、事件系統、內建狀態動作、動態屬性與雙向綁定、情境輔助函式,以及透過 createUIStream 與 createChatUI 實作的串流 UI。
適用情境
適用於使用 @json-render/svelte、從 JSON 規格建構 Svelte UI、建立元件目錄,或渲染 AI 生成規格的場合。
執行需求
需要 Svelte 5、@json-render/svelte 與 @json-render/core 套件,以及用於目錄結構定義的 Zod。串流輔助函式會呼叫 API 端點。不附帶指令碼,僅為說明文件。

@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:

ContextPurpose
StateContextShare state across components (JSON Pointer paths)
ActionContextHandle actions dispatched via the event system
VisibilityContextEnable conditional rendering based on state
ValidationContextForm 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");

來源與署名

來源:vercel-labs/json-render位於skills/svelte提交fc2a696

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架