vue について
@json-render/vueは、JSON仕様をVue 3のコンポーネントツリーに変換します。Vueでデータ駆動型UIを構築する開発者向けで、状態、アクション、表示条件、検証に対応し、外部ストアにも接続できます。
主な機能
- 01JSON仕様をVueコンポーネントツリーに変換
- 02状態バインディングと動的プロパティ式
- 03アクション・表示条件・検証のコンテキスト
- 04独自コンポーネントと名前付きスロットの登録
- 05外部状態ストアへの接続
- 06UI仕様のストリーミング
活用シーン
- 01VueアプリでJSONの画面仕様を描画
- 02状態に応じた表示制御とプロパティのバインド
- 03フォーム検証とコンポーネントアクションの処理
- 04APIからUI仕様をストリーム受信
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vercel-labs/ json-render --skill vueインストール後、Claude Code で/vueを入力して読み込まれたか確認します。
詳細
@json-render/vue
JSON 仕様をデータバインディング、表示制御、アクションに対応した Vue コンポーネントツリーに変換する Vue 3 レンダラーです。
インストール
npm install @json-render/vue @json-render/core zod
ピア依存関係:vue ^3.5.0 と zod ^4.0.0。
クイックスタート
カタログを作成する
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: {},
});
h() レンダー関数でレジストリを定義する
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),
},
});
名前付きスロット
"default" スロットには children を使用します。カタログで宣言したその他のスロット名には、要素のトップレベルにある slots オブジェクトを使用します。
{
"type": "Layout",
"props": {},
"children": ["main"],
"slots": {
"header": ["heading"],
"footer": ["actions"]
}
}
レジストリのコンポーネントには、Vue ネイティブのスロット関数が渡されます。slots.header?.()、slots.footer?.() などを使ってレンダリングします。slots.default?.() は仕様の children をレンダリングします。children コンテキストフィールドは、そのレンダリング結果を参照するための便利なエイリアスです。JSON 仕様では、デフォルトのコンテンツは children に記述し、slots に default エントリーを追加しないでください。
仕様をレンダリングする
<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>
プロバイダー
| プロバイダー | 目的 |
|---|---|
StateProvider | コンポーネント間で状態を共有します(JSON Pointer パス)。制御モードでは initialState または store を受け取ります。 |
ActionProvider | イベントシステム経由でディスパッチされたアクションを処理します |
VisibilityProvider | 状態に基づく条件付きレンダリングを有効にします |
ValidationProvider | フォームフィールドの検証を行います |
コンポーザブル
| コンポーザブル | 目的 |
|---|---|
useStateStore() | 状態コンテキストにアクセスします(state は ShallowRef、get、set、update) |
useStateValue(path) | 状態から単一の値を取得します |
useIsVisible(condition) | 表示条件を満たしているか確認します |
useActions() | アクションコンテキストにアクセスします |
useAction(binding) | 単一のアクションディスパッチ関数を取得します |
useFieldValidation(path, config) | フィールドの検証状態 |
useBoundProp(propValue, bindingPath) | $bindState/$bindItem の双方向バインディング |
注:useStateStore().state は ShallowRef<StateModel> を返します。アクセスするには state.value を使用してください。
外部ストア(StateStore)
入れ子になったリストでは、外側の repeat 内で repeat.statePath に { "$item": "field" } を設定できます。
StateStore を StateProvider に渡すと、json-render を Pinia、VueUse、または任意の状態管理機能に接続できます。
import { createStateStore, type StateStore } from "@json-render/vue";
const store = createStateStore({ count: 0 });
<StateProvider :store="store">
<Renderer :spec="spec" :registry="registry" />
</StateProvider>
動的なプロパティ式
プロパティは $state、$bindState、$cond、$template、$computed をサポートします。双方向バインディングには、自然な値プロパティに { "$bindState": "/path" } を指定します。
表示条件
{ "$state": "/user/isAdmin" }
{ "$state": "/status", "eq": "active" }
{ "$state": "/maintenance", "not": true }
[ cond1, cond2 ] // implicit AND
組み込みアクション
setState、pushState、removeState、validateForm は Vue スキーマに組み込まれており、ActionProvider によって処理されます。
{
"action": "setState",
"params": { "statePath": "/activeTab", "value": "settings" }
}
イベントシステム
コンポーネントは emit(event) を使ってイベントを発行するか、on(event) を使ってメタデータ(shouldPreventDefault、bound)を取得します。
ストリーミング
useUIStream と useChatUI は、API からストリーミングされる仕様に対応する Vue Ref を返します。
BaseComponentProps
カタログに依存しない再利用可能なコンポーネント向けです。
import type { BaseComponentProps } from "@json-render/vue";
const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) =>
h("div", null, [props.title, children]);
主なエクスポート
| エクスポート | 目的 |
|---|---|
defineRegistry | カタログから型安全なコンポーネントレジストリを作成します |
Renderer | レジストリを使用して仕様をレンダリングします |
schema | 要素ツリーのスキーマ(@json-render/vue/schema から) |
StateProvider、ActionProvider、VisibilityProvider、ValidationProvider | コンテキストプロバイダー |
useStateStore、useStateValue、useBoundProp | 状態コンポーザブル |
useActions、useAction | アクションコンポーザブル |
useFieldValidation、useIsVisible | 検証と表示制御 |
useUIStream、useChatUI | ストリーミング用コンポーザブル |
createStateStore | インメモリの StateStore を作成します |
BaseComponentProps | カタログに依存しないコンポーネントのプロパティ型 |