svelte

par vercel

Moteur de rendu Svelte 5 pour json-render qui transforme des spécifications JSON en arbres de composants Svelte. À utiliser avec @json-render/svelte pour construire des interfaces utilisateur Svelte à partir de JSON,…

npx skills add https://github.com/vercel-labs/json-render --skill svelte

@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

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");

Plus de skills de vercel

benchmark-sandbox
vercel
Exécute les scénarios d'évaluation de vercel-plugin dans des sandbox Vercel au lieu de panneaux WezTerm locaux. Provisionne des microVM éphémères avec Claude Code et le plugin préinstallé,…
official
emil-design-eng
vercel
Cette compétence encode la philosophie d'Emil Kowalski sur le polissage UI, la conception de composants, les décisions d'animation et les détails invisibles qui rendent un logiciel agréable.
official
vercel-react-best-practices
vercel
Directives d'optimisation des performances React et Next.js de l'équipe Vercel Engineering. Cette compétence doit être utilisée lors de l'écriture, de la révision ou du refactoring de code React/Next.js…
official
vercel-react-best-practices
vercel
Directives d'optimisation des performances React et Next.js de Vercel Engineering. Cette compétence doit être utilisée lors de l'écriture, de la révision ou du refactoring de code React/Next.js…
official
write-guide
vercel
Produire un guide technique qui enseigne un cas d'usage concret à travers des exemples progressifs. Les concepts ne sont introduits que lorsque le lecteur en a besoin.
official
release
vercel
Release vercel-plugin — exécuter les gates, incrémenter la version, générer les artefacts, commiter et pousser. Utiliser lorsqu'on demande de "release", "ship", "bump and push" ou "cut a release".
official
deepsec
vercel
Exécuter DeepSec sur un checkout de projet Vercel depuis dev3000. Utiliser pour la configuration DeepSec en un clic, l’amorçage du contexte du projet, le traitement limité de premier passage, et…
official
backport-pr
vercel
Effectuer un backport d'une pull request Next.js fusionnée depuis canary vers une branche de version précédente telle que next-16-2. Utiliser lorsque l'utilisateur demande de faire un backport, un cherry-pick ou d'ouvrir un…
official