JSON UI で最初にぶつかった問題
サーバーが送る JSON の画面構造を React コンポーネントとして描画するライブラリだ。デプロイせず構成を変えたいダッシュボード、動的フォーム、CMS などに利用できる。
当初はプロジェクトごとに再帰 renderer と状態管理を作り直していた。同じ問題を繰り返し解かなくて済むように、共通部分をパッケージへ切り出した。
JSON document → normalized store → React renderer → node-level updates

同じレイアウトを Document、Nodes、Store の視点で確認する Storybook inspector。
状態をノード単位に分ける
文書を ID 基準の entity map に正規化した。コンポーネントは自分のノードだけを購読し、updateNodeState(id) はその ID の購読者だけに通知する。サーバーから届く値は、各コンポーネントの Zod schema で検証する。
Toggle 一つが変わるとき
"use client";
import {
useSduiLayoutAction,
useSduiNodeSubscription,
} from "@lodado/sdui-template";
import { z } from "zod";
const toggleSchema = z.object({ checked: z.boolean() });
function Toggle({ id }: { id: string }) {
const { state } = useSduiNodeSubscription({
nodeId: id,
schema: toggleSchema,
});
const store = useSduiLayoutAction();
return (
<button
onClick={() =>
store.updateNodeState(id, { checked: !state.checked })
}
>
{state.checked ? "ON" : "OFF"}
</button>
);
}
ボタンを押しても文書全体ではなく、この Toggle を購読するコンポーネントだけが再レンダリングされる。
設計思想
サーバーは構造、クライアントは操作を担当する
サーバーはどのコンポーネントをどの順序で表示するかを決める。クリックや入力は React コンポーネントが処理する。JSON が React のあらゆる動作まで模倣し始めると、かえって変更しづらくなる。
文書全体ではなく、変わったノードを見る
JSON ツリーを nodes map と rootId に正規化する。ノード検索は ID で終わり、変更通知もその ID の購読者にだけ届く。大きな文書でも更新範囲を予測しやすい。
サーバー JSON を信頼しない
TypeScript の型はランタイム入力を守れない。custom component が自分の state を Zod schema で確認し、誤った JSON を renderer の深い場所ではなくコンポーネント境界で止める。
関係と例外を隠さない
別ノードの状態が必要なら reference で関係を示す。標準コンポーネントが合わなければ node ID または type 単位で override する。暗黙の global state や renderer の修正で迂回しないようにした。
すべての画面を SDUI にしない
サーバーが構造を変える理由のない静的画面なら、通常の React の方が単純だ。SDUI は構成可能なダッシュボード、動的フォーム、CMS など、サーバー制御が本当に必要な場所でだけ使う。
現在は npm パッケージとして公開し、モノレポ内の他プロジェクトでも利用している。