svelte — скилл для Claude Code: JSON-интерфейсы в Svelte
Помогает собирать интерфейсы Svelte 5 из спецификаций json-render и связывать компоненты с состоянием, событиями и действиями.
npx skills add vercel-labs/json-render --skill svelte -a claude-codeЧто делает svelte
Скилл описывает рендерер @json-render/svelte, который превращает JSON-спецификацию в дерево компонентов Svelte. Он подсказывает, как передать spec и реестр компонентов в Renderer, обернув его в JsonUIProvider. Это подходит для интерфейсов, чья структура приходит в JSON.
Внутри есть схема каталога через defineCatalog и zod, а также настройка реестра через defineRegistry. Компоненты получают props, дочерние элементы, emit, привязки и признак потоковой загрузки. Событие, вызванное через emit, связывается с действием в поле on элемента.
Скилл разбирает условия visible, действия setState, pushState, removeState, push и pop, а также двусторонние привязки через getBoundProp. Для потоковой генерации спецификации используется createUIStream. Нужны Svelte 5 и пакет @json-render/svelte; это повод сверить версию проекта перед добавлением кода.
Какие задачи решает
- отрендерить дерево Svelte-компонентов из JSON-спецификации
- описать каталог компонентов через
defineCatalog - связать событие компонента с действием из поля
on - привязать поле формы к пути состояния
- скрыть элемент по условию состояния
Примеры запросов агенту
- «Собери Svelte 5 интерфейс из JSON-спецификации через `@json-render/svelte`.»
- «Создай каталог `Button` и `Card` с `defineCatalog` и `zod`.»
- «Добавь в компонент Svelte привязку `getBoundProp` для поля формы.»
Как установить svelte
1Через Skills CLI (Vercel)
npx skills add vercel-labs/json-render --skill svelte -a claude-code2Вручную: положить файлы
~/.claude/skills/svelte/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/svelte/
1Через Skills CLI (Vercel)
npx skills add vercel-labs/json-render --skill svelte -a codex2Вручную: положить файлы
.agents/skills/svelte/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add vercel-labs/json-render --skill svelte -a cursor2Вручную: положить файлы
.cursor/skills/svelte/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install vercel-labs/json-render svelte2Вручную: положить файлы
.github/skills/svelte/SKILL.md1Через Skills CLI (Vercel)
npx skills add vercel-labs/json-render --skill svelte -a gemini-cli2Командой платформы
gemini skills install https://github.com/vercel-labs/json-render --path skills/svelteВручную: положить файлы
.devin/skills/svelte/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add vercel-labs/json-render --skill svelte -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
Из того же набора20
Весь набор →Похожие
Вопросы о svelte
Как установить svelte в Claude Code?
Выполните npx skills add vercel-labs/json-render --skill svelte -a claude-code, затем повторите npx skills add vercel-labs/json-render --skill svelte -a claude-code. Файл скилла расположен в ~/.claude/skills/svelte/SKILL.md.
Что делает svelte?
Скилл объясняет работу @json-render/svelte: он строит дерево компонентов Svelte 5 по спецификации json-render. В нём разобраны каталог, реестр, состояние, действия и события.
Как передавать события из компонента в действие?
Компонент вызывает emit с именем события, а поле on в JSON-элементе связывает это имя с действием. Обработчики действий можно зарегистрировать через defineRegistry.
Исходный текст
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