Skip to content
cccskills
FreePublished on GitHub

svelte

Turn JSON screen descriptions into Svelte 5 interfaces. This skill covers component catalogs, registration, shared state, form bindings that keep inputs and data synchronized, conditional display, and streaming AI-generated interfaces as they arrive.

OriginalShow the original 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.

See how to install

Good for

  • Render a Svelte interface from JSON
  • Define and register reusable screen components
  • Synchronize form inputs with shared state
  • Display streaming UI and chat responses

Files included (1)

  • SKILL.md7.1 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

@json-render/svelte

Svelte 5 renderer that converts json-render specs into Svelte component trees.

Quick Start

<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

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

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
}
<!-- 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>
<!-- 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

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:

{
  "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:

<!-- 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>
{
  "type": "Button",
  "props": { "label": "Submit" },
  "on": { "press": { "action": "submit" } }
}

Built-in Actions

The setState action is handled automatically and updates the state model:

{
  "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:

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

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

const chat = createChatUI({ api: "/api/chat-ui" });
await chat.send("Build a settings panel");

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

codegen

FreeSummary

Helps build custom code exporters from json-render UI specifications. Covers walking the UI tree, collecting components, state references, and actions, and formatting property values for JSX, the markup syntax used in React.

  • Build a custom exporter for Next.js or Remix
  • List components and actions used in a UI specification
  • Convert property values into JSX attributes
vercel-labs/json-render18.7KUpdated Oct 14, 2026

core

FreeSummary

Define the structure and available components for AI-generated JSON screens or videos. Build generation prompts, process incremental updates, connect values to application state, and validate or repair definitions before passing them to a renderer.

  • Define allowed components for AI-generated screens
  • Build prompts for generating or editing JSON definitions
  • Assemble definitions from streamed AI responses
vercel-labs/json-render18.7KUpdated Oct 14, 2026

devtools

FreeSummary

Debug json-render interfaces with an inspector panel that shows screen specifications, editable runtime state, action history, and streamed updates. Select a rendered page element to locate its definition and investigate display or interaction problems.

  • Investigate missing or incorrectly displayed elements
  • Inspect and edit application state at runtime
  • Review action results and errors
vercel-labs/json-render18.7KUpdated Oct 14, 2026

devtools-react

FreeSummary

Set up an inspection panel in React json-render apps to examine UI definitions, edit state, and review action and stream timelines. The skill also covers opening or toggling the panel and recording events from React code.

  • Adding an inspection panel to a React app
  • Inspecting UI definitions, state, and action history
  • Opening or toggling the panel from React code
vercel-labs/json-render18.7KUpdated Oct 14, 2026

devtools-solid

FreeSummary

Add and troubleshoot the json-render inspection panel in SolidJS applications. This skill covers panel setup for examining specifications, editing state, reviewing action and stream timelines, browsing component catalogs, and selecting page elements for inspection.

  • Add an inspection panel to a SolidJS app
  • Inspect JSON specifications and edit application state
  • Review action and stream timelines
vercel-labs/json-render18.7KUpdated Oct 14, 2026

devtools-svelte

FreeSummary

Add and troubleshoot a developer panel in Svelte 5 json-render apps. Inspect JSON screen definitions, edit application state, review action and streaming timelines, browse component catalogs, and select elements directly from the page.

  • Add a developer panel to a Svelte app
  • Inspect screen definitions and edit application state
  • Review action and streaming timelines
vercel-labs/json-render18.7KUpdated Oct 14, 2026

See all skills by vercel-labs

Report a problem with this skill