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 从公开仓库中收录这些内容。

举报或申请下架