Vue

by vercel-labsfc2a696a50a3No license18K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated yesterday

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.

FeaturedInstructions onlySoftware Development
AI-generated overview

Reference guide for @json-render/vue, a Vue 3 renderer that turns JSON specs into Vue component trees.

What it does
This skill documents @json-render/vue, a Vue 3 renderer that converts JSON specs into Vue component trees with data binding, visibility conditions, and actions. It shows how to define a catalog with Zod schemas, build a component registry using Vue h() render functions, and render specs through providers such as StateProvider, ActionProvider, VisibilityProvider, and ValidationProvider. It also covers composables, named slots, dynamic prop expressions, built-in actions, the event system, streaming helpers, and external state stores.
When to use it
Use it when building Vue UIs from JSON specs, working with @json-render/vue, defining Vue component registries, or rendering AI-generated specs in Vue. It is a reference for developers integrating this renderer rather than a task workflow.
Requirements
Requires the npm packages @json-render/vue, @json-render/core, and zod, with peer dependencies vue ^3.5.0 and zod ^4.0.0. It ships no scripts; it is documentation only.

@json-render/vue

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

Installation

bash
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

typescript
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

typescript
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:

json
{  "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

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

ProviderPurpose
StateProviderShare state across components (JSON Pointer paths). Accepts initialState or store for controlled mode.
ActionProviderHandle actions dispatched via the event system
VisibilityProviderEnable conditional rendering based on state
ValidationProviderForm field validation

Composables

ComposablePurpose
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:

typescript
import { createStateStore, type StateStore } from "@json-render/vue";
const store = createStateStore({ count: 0 });
vue
<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

typescript
{ "$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:

json
{  "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:

typescript
import type { BaseComponentProps } from "@json-render/vue";
const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) =>  h("div", null, [props.title, children]);

Key Exports

ExportPurpose
defineRegistryCreate a type-safe component registry from a catalog
RendererRender a spec using a registry
schemaElement tree schema (from @json-render/vue/schema)
StateProvider, ActionProvider, VisibilityProvider, ValidationProviderContext providers
useStateStore, useStateValue, useBoundPropState composables
useActions, useActionAction composables
useFieldValidation, useIsVisibleValidation and visibility
useUIStream, useChatUIStreaming composables
createStateStoreCreate in-memory StateStore
BaseComponentPropsCatalog-agnostic component props type

Source and attribution

Source:vercel-labs/json-renderinskills/vueat commitfc2a696

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal