Skip to content
Development
Skill

/vue

Vue 3 renderer for json-render. Use when building Vue UIs from JSON specs, working with @json-render/vue, defining Vue component registries, or rendering AI-generated specs in Vue.

From plugin
json-render
16k27 skills
Install
$ npx -y skills add vercel-labs/json-render --skill vue --agent claude-code

How 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/vue

Context preview

The summary Claude sees to decide when to auto-load this skill.

Vue 3 renderer for json-render. Use when building Vue UIs from JSON specs, working with @json-render/vue, defining Vue component registries, or rendering AI-generated specs in Vue.

SKILL.md

vue.SKILL.md
name: vue
description: Vue 3 renderer for json-render. Use when building Vue UIs from JSON specs, working with @json-render/vue, defining Vue component registries, or rendering AI-generated specs in Vue.

@json-render/vue

Vue 3 renderer that converts JSON specs into Vue component trees with data binding, visibility, and actions.

Installation

npm install @json-render/vue @json-render/core zod

Peer dependencies: `vue ^3.5.0` and `zod ^4.0.0`.

Quick Start

Create a Catalog

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/vue/schema";
import { z } from "zod";

export const catalog = defineCatalog(schema, {
  components: {
    Card: {
      props: z.object({ title: z.string(), description: z.string().nullable() }),
      slots: ["default"],
      description: "A card container",
    },
    Layout: {
      props: z.object({}),
      slots: ["default", "header", "footer"],
      description: "Layout with named content regions",
    },
    Button: {
      props: z.object({ label: z.string(), action: z.string() }),
      description: "A clickable button",
    },
  },
  actions: {},
});

Define Registry with h() Render Functions

import { h } from "vue";
import { defineRegistry } from "@json-render/vue";
import { catalog } from "./catalog";

export const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) =>
      h("div", { class: "card" }, [
        h("h3", null, props.title),
        props.description ? h("p", null, props.description) : null,
        children,
      ]),
    Layout: ({ slots }) =>
      h("div", null, [
        h("header", null, slots.header?.()),
        h("main", null, slots.default?.()),
        h("footer", null, slots.footer?.()),
      ]),
    Button: ({ props, emit }) =>
      h("button", { onClick: () => emit("press") }, props.label),
  },
});

Named Slots

Use `children` for the `"default"` slot. Use the element's top-level `slots` object for other slot names declared by the catalog:

{
  "type": "Layout",
  "props": {},
  "children": ["main"],
  "slots": {
    "header": ["heading"],
    "footer": ["actions"]
  }
}

Registry components receive Vue-native slot functions. Render them with `slots.header?.()`, `slots.footer?.()`, and so on. `slots.default?.()` renders the spec's `children`; the `children` context field is a convenience alias for that rendered result. In the JSON spec, keep default content in `children` rather than adding a `default` entry to `slots`.

Render Specs

<script setup lang="ts">
import { StateProvider, ActionProvider, Renderer } from "@json-render/vue";
import { registry } from "./registry";

const spec = { root: "card-1", elements: { /* ... */ } };
</script>

<template>
  <StateProvider :initial-state="{ form: { name: '' } }">
    <ActionProvider :handlers="{ submit: handleSubmit }">
      <Renderer :spec="spec" :registry="registry" />
    </ActionProvider>
  </StateProvider>
</template>

Providers

| Provider | Purpose | |----------|---------| | `StateProvider` | Share state across components (JSON Pointer paths). Accepts `initialState` or `store` for controlled mode. | | `ActionProvider` | Handle actions dispatched via the event system | | `VisibilityProvider` | Enable conditional rendering based on state | | `ValidationProvider` | Form field validation |

Composables

| Composable | Purpose | |------------|---------| | `useStateStore()` | Access state context (`state` as `ShallowRef`, `get`, `set`, `update`) | | `useStateValue(path)` | Get single value from state | | `useIsVisible(condition)` | Check if a visibility condition is met | | `useActions()` | Access action context | | `useAction(binding)` | Get a single action dispatch function | | `useFieldValidation(path, config)` | Field validation state | | `useBoundProp(propValue, bindingPath)` | Two-way binding for `$bindState`/`$bindItem` |

Note: `useStateStore().state` returns a `ShallowRef<StateModel>` — use `state.value` to access.

External Store (StateStore)

Nested lists can set `repeat.statePath` to `{ "$item": "field" }` inside an enclosing repeat.

Pass a `StateStore` to `StateProvider` to wire json-render to Pinia, VueUse, or any state management:

import { createStateStore, type StateStore } from "@json-render/vue";

const store = createStateStore({ count: 0 });
<StateProvider :store="store">
  <Renderer :spec="spec" :registry="registry" />
</StateProvider>

Dynamic Prop Expressions

Props support `$state`, `$bindState`, `$cond`, `$template`, `$computed`. Use `{ "$bindState": "/path" }` on the natural value prop for two-way binding.

Visibility Conditions

{ "$state": "/user/isAdmin" }
{ "$state": "/status", "eq": "active" }
{ "$state": "/maintenance", "not": true }
[ cond1, cond2 ]  // implicit AND

Built-in Actions

`setState`, `pushState`, `removeState`, and `validateForm` are built into the Vue schema and handled by `ActionProvider`:

{
  "action": "setState",
  "params": { "statePath": "/activeTab", "value": "settings" }
}

Event System

Components use `emit(event)` to fire events, or `on(event)` for metadata (`shouldPreventDefault`, `bound`).

Streaming

`useUIStream` and `useChatUI` return Vue Refs for streaming specs from an API.

BaseComponentProps

For catalog-agnostic reusable components:

import type { BaseComponentProps } from "@json-render/vue";

const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) =>
  h("div", null, [props.title, children]);

Key Exports

| Export | Purpose | |--------|---------| | `defineRegistry` | Create a type-safe component registry from a catalog | | `Renderer` | Render a spec using a registry | | `schema` | Element tree schema (from `@json-render/vue/schema`) | | `StateProvider`, `ActionProvider`, `VisibilityProvider`, `ValidationPro

Read more
Ships withjson-render

The Generative UI framework. Generate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output.

Get the whole plugin
Stats
16,170
Stars
874
Forks
Active
Maintenance
TypeScript
Language
Apache-2.0
License
2d ago
Last commit
8mo ago
Created

Repo: vercel-labs/json-render

Other skills on json-render.