solid について
@json-render/solidは、json-renderの仕様を細粒度リアクティブなSolidコンポーネントツリーに変換します。構造化UIを構築する開発者向けで、状態管理や検証のほか、ストリーミングUIにも対応します。
主な機能
- 01リアクティブなSolidコンポーネント描画
- 02状態・表示・検証のプロバイダー
- 03状態とフォームの組み込みアクション
- 04動的プロパティと状態バインディング
- 05ストリーミングUIとチャット生成
活用シーン
- 01SolidアプリでUI仕様を描画
- 02入力をリアクティブな状態にバインド
- 03フィールドとフォームを検証
- 04ストリーム生成UIを表示
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ vercel-labs/ json-render --skill solidインストール後、Claude Code で/solidを入力して読み込まれたか確認します。
詳細
@json-render/solid
@json-render/solid は、json-render の仕様を細粒度リアクティブな Solid コンポーネントツリーにレンダリングします。
クイックスタート
import { Renderer, JSONUIProvider } from "@json-render/solid";
import type { Spec } from "@json-render/solid";
import { registry } from "./registry";
export function App(props: { spec: Spec | null }) {
return (
<JSONUIProvider registry={registry} initialState={{}}>
<Renderer spec={props.spec} registry={registry} />
</JSONUIProvider>
);
}
カタログの作成
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/solid/schema";
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",
},
},
actions: {
submit: { description: "Submit data" },
},
});
コンポーネントの定義
コンポーネントは、レンダラーから ComponentRenderProps を受け取ります。
interface ComponentRenderProps<P = Record<string, unknown>> {
element: UIElement<string, P>;
children?: JSX.Element;
emit: (event: string) => void;
on: (event: string) => EventHandle;
bindings?: Record<string, string>;
loading?: boolean;
}
例:
import type { BaseComponentProps } from "@json-render/solid";
export function Button(props: BaseComponentProps<{ label: string }>) {
return (
<button onClick={() => props.emit("press")}>{props.props.label}</button>
);
}
レジストリの作成
import { defineRegistry } from "@json-render/solid";
import { catalog } from "./catalog";
import { Card } from "./Card";
import { Button } from "./Button";
const { registry, handlers, executeAction } = defineRegistry(catalog, {
components: {
Card,
Button,
},
actions: {
submit: async (params, setState, state) => {
// custom action logic
},
},
});
仕様の構造
{
"root": "card1",
"elements": {
"card1": {
"type": "Card",
"props": { "title": "Hello" },
"children": ["btn1"]
},
"btn1": {
"type": "Button",
"props": { "label": "Click me" },
"on": {
"press": { "action": "submit" }
}
}
}
}
プロバイダー
StateProvider:storeを介した状態モデルの読み取り/書き込みと制御モードVisibilityProvider:visible条件を評価しますValidationProvider:フィールドの検証とvalidateFormの統合ActionProvider:組み込みアクションとカスタムアクションを実行しますJSONUIProvider:プロバイダーをまとめてラップします
フック
useStateStore、useStateValue、useStateBindinguseVisibility、useIsVisibleuseActions、useActionuseValidation、useOptionalValidation、useFieldValidationuseBoundPropuseUIStream、useChatUI
組み込みアクション
ActionProvider が自動的に処理します。
setStatepushStateremoveStatevalidateForm
動的プロパティとバインディング
ネストしたリストでは、外側の repeat 内で repeat.statePath に { "$item": "field" } を設定できます。
次の式形式がサポートされています。
{"$state": "/path"}{"$bindState": "/path"}{"$bindItem": "field"}{"$template": "Hi ${/user/name}"}{"$computed": "fn", "args": {...}}{"$cond": <condition>, "$then": <value>, "$else": <value>}
書き込み可能なバインド値には、コンポーネント内で useBoundProp を使用します。
import { useBoundProp, type BaseComponentProps } from "@json-render/solid";
function Input(props: BaseComponentProps<{ value?: string }>) {
const [, setValue] = useBoundProp(props.props.value, props.bindings?.value);
return (
<input
value={String(props.props.value ?? "")}
onInput={(e) => setValue(e.currentTarget.value)}
/>
);
}
useBoundProp は、スカラー値と書き戻し用のセッターを返します。後続の状態更新がリアクティブに保たれるよう、JSX 内で解決済みのプロパティを読み取ってください。
useStateValue、useStateBinding、および useFieldValidation の state / errors / isValid フィールドは、Solid のリアクティブなアクセサーです。JSX、createMemo、または createEffect 内で関数として呼び出してください。
Solid のリアクティビティに関するルール
- 値のリアクティビティを保つ必要がある場合、関数シグネチャでコンポーネントのプロパティを分割代入しないでください。
- 変化する値の読み取りは、JSX 式、
createMemo、またはcreateEffect内に記述してください。 - コンテキスト値はゲッターベースのオブジェクトを介して公開されるため、利用側は常に最新のシグナルを参照できます。
UI のストリーミング
import { useUIStream, Renderer } from "@json-render/solid";
const stream = useUIStream({ api: "/api/generate-ui" });
await stream.send("Create a support dashboard");
<Renderer
spec={stream.spec}
registry={registry}
loading={stream.isStreaming}
/>;
チャットと UI 生成のワークフローには useChatUI を使用します。
コメント
Markdown 対応