mcpField
R
react

react

github.com/vercel-labs開発18.8k16.6k 回インストール5 日前に更新
リポジトリ

react について

@json-render/reactは、JSONの要素ツリー仕様をReactコンポーネントツリーに変換するレンダラーです。データ駆動型UIの開発者向けに、型付きレジストリ、状態バインディング、条件表示、イベント、フォーム検証を備えています。

主な機能

  1. 01JSON仕様をReactコンポーネントツリーに変換
  2. 02カタログと型付きレジストリの定義
  3. 03名前付きスロットと条件表示に対応
  4. 04状態の読み取りと双方向バインディング
  5. 05イベント、アクション、状態監視の処理
  6. 06フォームフィールドの検証

活用シーン

  1. 01JSON仕様から動的なUIを構築
  2. 02再利用可能なカタログコンポーネントを作成
  3. 03フォーム入力をアプリケーション状態に連携
  4. 04状態変化に応じてUIアクションを実行

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/reactを入力して読み込まれたか確認します。

詳細

@json-render/react

JSON 仕様を React コンポーネントツリーに変換する React レンダラーです。

クイックスタート

import { defineRegistry, Renderer } from "@json-render/react";
import { catalog } from "./catalog";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) => <div>{props.title}{children}</div>,
  },
});

function App({ spec }) {
  return <Renderer spec={spec} registry={registry} />;
}

カタログの作成

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

// Create catalog with props schemas
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() }),
      slots: ["default"],
      description: "Card container with title",
    },
    Layout: {
      props: z.object({}),
      slots: ["default", "header", "footer"],
      description: "Layout with named content regions",
    },
  },
});

// Define component implementations with type-safe props
const { registry } = defineRegistry(catalog, {
  components: {
    Button: ({ props }) => (
      <button className={props.variant}>{props.label}</button>
    ),
    Card: ({ props, children }) => (
      <div className="card">
        <h2>{props.title}</h2>
        {children}
      </div>
    ),
    Layout: ({ children, slots }) => (
      <div>
        <header>{slots?.header}</header>
        <main>{children}</main>
        <footer>{slots?.footer}</footer>
      </div>
    ),
  },
});

仕様の構造(要素ツリー)

React スキーマでは、要素ツリー形式を使用します。

{
  "root": {
    "type": "Card",
    "props": { "title": "Hello" },
    "children": [{ "type": "Button", "props": { "label": "Click me" } }]
  }
}

名前付きスロット

"default" スロットには children を使用します。カタログで宣言されたその他のスロット名には、要素のトップレベルにある slots オブジェクトを使用します。

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

レジストリコンポーネントは、名前付きコンテンツを slots?.header、slots?.footer などとして受け取ります。slots.default は使用しないでください。

表示条件

要素に visible を設定すると、状態に応じて表示/非表示を切り替えられます。新しい構文:{ "$state": "/path" }、{ "$state": "/path", "eq": value }、{ "$state": "/path", "not": true }、AND 条件には { "$and": [cond1, cond2] }、OR 条件には { "$or": [cond1, cond2] } を使用します。ヘルパー:visibility.when("/path")、visibility.unless("/path")、visibility.eq("/path", val)、visibility.and(cond1, cond2)、visibility.or(cond1, cond2)。

プロバイダー

プロバイダー目的
StateProviderコンポーネント間で状態を共有します(JSON Pointer パス)。制御モード用にオプションの store プロパティを指定できます。
ActionProviderイベントシステムを介してディスパッチされたアクションを処理します
VisibilityProvider状態に基づく条件付きレンダリングを有効にします
ValidationProviderフォームフィールドを検証します
外部ストア(制御モード)

StateProvider(または JSONUIProvider/createRenderer)に StateStore を渡すと、外部の状態管理(Redux、Zustand、XState など)を使用できます。

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

const store = createStateStore({ count: 0 });

<StateProvider store={store}>{children}</StateProvider>;

// Mutate from anywhere — React re-renders automatically:
store.set("/count", 1);

store を指定すると、initialState と onStateChange は無視されます。

動的プロパティ式

任意のプロパティ値にデータ駆動型の式を指定できます。コンポーネントにプロパティが渡される前に、レンダラーが式を解決します。

  • { "$state": "/state/key" } - 状態モデルから値を読み取ります(片方向読み取り)
  • { "$bindState": "/path" } - 双方向バインディングです。状態を読み取り、変更を状態に書き戻せます。フォームコンポーネントの自然な値プロパティ(value、checked、pressed など)に使用します。
  • { "$bindItem": "field" } - 繰り返し項目のフィールドに双方向でバインドします。繰り返しスコープ内で使用します。
  • フィルタリングされたリスト:同じコンテナで repeat と $item の表示条件を組み合わせると、一致する項目のみがレンダリングされます:{ "repeat": { "statePath": "/tasks", "key": "id" }, "visible": { "$item": "status", "eq": "todo" }, "children": ["task-card"] }。AND で結合された $state 条件はコンテナ全体の表示を制御し、$item/$index 条件は項目をフィルタリングします。
  • ネストされたリスト:繰り返しの中で { "repeat": { "statePath": { "$item": "comments" }, "key": "id" } } を使用すると、外側の項目にある配列を反復処理できます。
  • { "$cond": <condition>, "$then": <value>, "$else": <value> } - 条件付きの値
  • { "$template": "Hello, ${/name}!" } - 状態の値を文字列に補間します
  • { "$computed": "fn", "args": { ... } } - 登録済みの関数を、解決済みの引数で呼び出します
{
  "type": "Input",
  "props": {
    "value": { "$bindState": "/form/email" },
    "placeholder": "Email"
  }
}

コンポーネントは、双方向バインディングに statePath プロパティを使用しません。代わりに、自然な値プロパティに { "$bindState": "/path" } を指定します。

コンポーネントには、解決済みのプロパティが渡されます。双方向バインドされたプロパティには、レンダラーが提供する bindings マップとともに useBoundProp フックを使用します。

JSONUIProvider または createRenderer の functions プロパティを介して、$computed 関数を登録します。

<JSONUIProvider
  functions={{ fullName: (args) => `${args.first} ${args.last}` }}
>

イベントシステム

コンポーネントは emit を使って名前付きイベントを発行するか、on() を使ってメタデータ付きのイベントハンドルを取得します。要素の on フィールドは、イベントとアクションバインディングを対応付けます。

// Simple event firing
Button: ({ props, emit }) => (
  <button onClick={() => emit("press")}>{props.label}</button>
),

// Event handle with metadata (e.g. preventDefault)
Link: ({ props, on }) => {
  const click = on("click");
  return (
    <a href={props.href} onClick={(e) => {
      if (click.shouldPreventDefault) e.preventDefault();
      click.emit();
    }}>{props.label}</a>
  );
},
{
  "type": "Button",
  "props": { "label": "Submit" },
  "on": { "press": { "action": "submit" } }
}

on() が返す EventHandle には、emit()、shouldPreventDefault(ブール値)、bound(ブール値)があります。

状態監視

要素では、状態値が変化したときにアクションを実行するため、watch フィールド(type/props/children と同じ階層のトップレベル)を宣言できます。

{
  "type": "Select",
  "props": {
    "value": { "$bindState": "/form/country" },
    "options": ["US", "Canada"]
  },
  "watch": { "/form/country": { "action": "loadCities" } },
  "children": []
}

組み込みアクション

setState、pushState、removeState、validateForm アクションは React スキーマに組み込まれており、ActionProvider によって自動的に処理されます。カタログの actions で宣言しなくても、AI のプロンプトに挿入されます。

{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }
{ "action": "validateForm", "params": { "statePath": "/formResult" } }

validateForm は登録されたすべてのフィールドを検証し、{ valid, errors } を状態に書き込みます。

注:アクションのパラメーターにある statePath(例:setState.statePath)は、変更対象のパスを指定します。コンポーネントの props で双方向バインディングを行うには、statePath ではなく値の prop に { "$bindState": "/path" } を使用します。

useBoundProp

双方向バインディングが必要なフォームコンポーネントでは、prop に { "$bindState": "/path" } または { "$bindItem": "field" } を使用したときにレンダラーから提供される bindings マップとともに useBoundProp を使用します。

import { useBoundProp } from "@json-render/react";

Input: ({ element, bindings }) => {
  const [value, setValue] = useBoundProp<string>(
    element.props.value,
    bindings?.value
  );
  return (
    <input
      value={value ?? ""}
      onChange={(e) => setValue(e.target.value)}
    />
  );
},

useBoundProp(propValue, bindingPath) は [value, setValue] を返します。value は解決済みの prop で、setValue はバインドされた状態のパスに値を書き戻します(バインドされていない場合は何もしません)。

BaseComponentProps

特定のカタログに依存しない再利用可能なコンポーネントライブラリ(例:@json-render/shadcn)を構築する場合に使用します。

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

const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
  <div>{props.title}{children}</div>
);

defineRegistry

defineRegistry は、カタログでアクションが宣言されている場合にのみ actions フィールドを必須にします。actions: {} を指定したカタログでは、省略できます。

主なエクスポート

エクスポート用途
defineRegistryカタログから型安全なコンポーネントレジストリを作成します
Rendererレジストリを使用して spec をレンダリングします
schema要素ツリースキーマ(組み込みの状態アクション:setState、pushState、removeState、validateForm を含む)
useStateStore状態コンテキストにアクセスします
useStateValue状態から単一の値を取得します
useBoundProp$bindState/$bindItem 式の双方向バインディング
useActionsアクションコンテキストにアクセスします
useAction単一のアクションディスパッチ関数を取得します
useOptionalValidationuseValidation の例外をスローしないバリアント(プロバイダーがない場合は null を返します)
useUIStreamAPI エンドポイントから spec をストリーミングします
createStateStoreフレームワークに依存しないインメモリの StateStore を作成します
StateStore外部の状態管理を接続するためのインターフェース
BaseComponentProps再利用可能なコンポーネントライブラリ向けのカタログ非依存の基本型
EventHandleイベントハンドル型(emit、shouldPreventDefault、bound)
ComponentContext型付きコンポーネントコンテキスト(カタログ対応)

コメント