Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

svelte — Claude Code skill: JSON UI rendering in Svelte

Guides AI coding agents through building Svelte 5 component trees from json-render specs, including state, events, bindings, and catalogs.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add vercel-labs/json-render --skill svelte -a claude-code
Open on GitHub

What svelte does

This skill documents the @json-render/svelte renderer, which turns a JSON spec into a Svelte component tree. It shows how to pass a spec and component registry to Renderer inside JsonUIProvider. Use it when the UI structure is delivered as JSON.

It covers component catalogs with defineCatalog and zod, plus registry setup with defineRegistry. Components can receive props, children, emit, bindings, and a streaming flag. An event emitted by a component is connected to an action through the element's on field.

The skill also covers visible conditions, setState, pushState, removeState, push, and pop, along with two-way bindings via getBoundProp. createUIStream handles streamed specs. You need Svelte 5 and @json-render/svelte, so check the project version before applying its code.

What it helps with

  • render a Svelte component tree from a JSON spec
  • define a component catalog with defineCatalog
  • map a component event to an action in on
  • bind a form field to a state path
  • show or hide an element from state

Example prompts for the agent

  • "Build a Svelte 5 UI from a JSON spec with `@json-render/svelte`."
  • "Create a `Button` and `Card` catalog using `defineCatalog` and `zod`."
  • "Add a `getBoundProp` binding for a form field in this Svelte component."

How to install svelte

1Skills CLI (Vercel)

npx skills add vercel-labs/json-render --skill svelte -a claude-code

2Manually: put the files in place

~/.claude/skills/svelte/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/svelte/

From the same set20

The whole set →
react vercel-labs This skill turns json-render JSON specs into React component trees. Use it when building JSON-driven UIs or defining a component catalog for generated specs. Claude Code CX Codex Cursor +1 16,300installs core vercel-labs Defines the schema and component catalog for json-render and builds the system prompt that makes an AI emit JSON UI specs. Claude Code CX Codex Cursor +1 15,000installs codegen vercel-labs Utilities for generating code from json-render UI trees. Build custom code exporters for Next.js, Remix, and other frameworks, traverse specs, and serialize props. Claude Code CX Codex Cursor +1 8,900installs next vercel-labs Converts JSON specs into Next.js apps with routes, layouts, SSR, and metadata. Handy when building AI-generated multi-page apps with @json-render/next. Claude Code CX Codex Cursor +1 8,600installs devtools vercel-labs Debug panel for json-render apps: component tree, state editor, actions log, streaming patches, and element picker. Claude Code CX Codex Cursor +1 8,300installs directives vercel-labs Pre-built directives for json-render: formatting, math, string handling, i18n. Wire them into your catalog and renderer with one import. Claude Code CX Codex Cursor +1 8,000installs mcp vercel-labs This skill helps build MCP servers that render interactive json-render UIs in Claude, ChatGPT, Cursor, and VS Code. Claude Code CX Codex Cursor +1 5,800installs react-native vercel-labs This skill turns JSON specs into native React Native mobile UIs. Use it when screens need state bindings, conditional visibility, and actions. Claude Code CX Codex Cursor +1 3,200installs

Similar

ce-polish EveryInc Refines a working feature through browser feedback: you point out what needs work, the skill edits the code, hot reload shows the result. Claude Code CX Codex Cursor +1 1,100installs react-audit-grep-patterns official github Verified grep pattern library for auditing React projects before upgrading to React 18.3.1 or 19. It spots deprecated APIs and syntax breaking changes. GitHub Copilot 854installs swiftui-ui-patterns official OpenAI This skill helps build and refactor SwiftUI interfaces, from navigation and state to layouts and controls. Use it for new screens or changes to an existing project. CX Codex GP ChatGPT 114installs shopify-apps sickn33 Patterns for Shopify app development with Remix and React Router. Guides App Bridge setups, Polaris UI components, billing flows, and webhooks. Claude Code CX Codex Cursor +1 1,000installs web-typography wondelai This skill helps you select and configure typefaces for web projects: from choosing a font family to optimizing size, line height, and measure. Use it when you need readable typography or want to find a well-matched font pair. Claude Code CX Codex Cursor +1 9,200installs libraries-dev Jakubantalik Selects and integrates Libraries.dev React UI effects for AI interfaces, covering loaders, voice glows, animated borders, and bot avatars. Claude Code CX Codex Cursor +1 991installs frontend-design official anthropics This skill guides intentional visual design for new interfaces and redesigns. Use it when a generic generated UI is not enough. Claude Code Claude.ai 1,025,790installs design-taste-frontend Leonxlnx This skill directs the design of public pages and work screens, then checks the result in a browser through screenshots. Use it when an interface feels generic or unfinished. Claude Code CX Codex Cursor +1 673,100installs

Questions about svelte

How do I install svelte in Claude Code?

Run npx skills add vercel-labs/json-render --skill svelte -a claude-code, then run npx skills add vercel-labs/json-render --skill svelte -a claude-code again. The skill file is at ~/.claude/skills/svelte/SKILL.md.

What does svelte do?

The skill explains @json-render/svelte, which renders json-render specifications as Svelte 5 component trees. It covers catalogs, registries, state, actions, and events.

How are component events connected to actions?

A component calls emit with an event name, and the JSON element's on field maps that event to an action. Action handlers can be registered with defineRegistry.

Source text

SKILL.md · Apache-2.0
---
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");
```

© vercel-labs · Apache-2.0 · vercel-labs/json-render

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor