/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.
$ npx -y skills add vercel-labs/json-render --skill svelte --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/svelte
Context preview
The summary Claude sees to decide when to auto-load this skill.
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.
SKILL.md
svelte.SKILL.mdname: 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
<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:
| 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:
<!-- 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
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(() =>
Read more
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
<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:
| 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:
<!-- 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
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(() =>
The Generative UI framework. Generate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output.
Repo: vercel-labs/json-render
Other skills on json-render.
- /codegen
Code generation utilities for json-render. Use when generating code from UI specs, building custom code exporters, traversing specs, or serializing props for @json-render/codegen.
Open skill - /core
Core package for defining schemas, catalogs, and AI prompt generation for json-render. Use when working with @json-render/core, defining schemas, creating catalogs, or building JSON specs for UI/video generation.
Open skill - /devtools
Drop-in inspector panel for any json-render app. Use when the user wants to debug a generative UI, inspect the spec tree, edit state at runtime, see dispatched actions, follow stream patches live, browse a catalog, or pick DOM elements to find their spec keys. Triggers include
Open skill - /directives
Pre-built custom directives for json-render — formatting, math, string manipulation, and i18n. Use when working with @json-render/directives, defining custom directives with defineDirective, or adding $format, $math, $concat, $count, $truncate, $pluralize, $join, or $t to specs.
Open skill - /image
Image renderer for json-render that turns JSON specs into SVG and PNG images via Satori. Use when working with @json-render/image, generating OG images from JSON, creating social cards, or rendering AI-generated image specs.
Open skill - /ink
Ink terminal renderer for json-render that turns JSON specs into interactive terminal UIs. Use when working with @json-render/ink, building terminal UIs from JSON, creating terminal component catalogs, or rendering AI-generated specs in the terminal.
Open skill

