Generative UIを爆速で実装できる「OpenUI」が面白そう
Generative UIを一から実装するとコンポーネントの管理やストリーミングの制御がめちゃくちゃ面倒なのだが、それを綺麗に共通化・隠蔽してくれるフレームワーク「OpenUI」
最近話題の Generative UI(生成AIが動的にUIを生成してユーザーに提示する技術)。 一から実装するとコンポーネントの管理やストリーミングの制御がめちゃくちゃ面倒なのだが、それを綺麗に共通化・隠蔽してくれるフレームワーク「OpenUI」というものを見つけたので、気になったポイントをメモ。
そもそも「OpenUI」とは?
Generative UIの実装に特化した軽量なフレームワーク。 AI(LLM)から返ってきた構造化データ(JSONなど)をキャッチして、フロントエンド側で動的にReact(Next.js)などのコンポーネントへマッピングする処理をいい感じに共通化してくれる。
ざっくりメリット
- ボイラープレートの削減: ストリーミング処理やローディング状態の管理、コンポーネントの動的レンダリングに必要な定型コードがほぼ不要になる。
- 特定のLLMに依存しない: OpenAI、Anthropic(Claude)、Geminiなど、主要なプロバイダやVercel AI SDK等と組み合わせて柔軟に使える(インターフェースが抽象化されている)。
- UIの型安全性の担保: 生成されるUIのスキーマ(引数やプロパティ)を定義しやすく、TypeScriptとの相性も良さそう。
どうやって動くのか?(処理の流れ)
大まかな流れは以下の3ステップ。
- バックエンド(AI側): 「ユーザーの要求(例:『進捗グラフを見せて』)」に対して、LLMに関数呼び出し(Tool Calling / Structured Outputs)を行わせ、画面描画に必要なパラメータ(JSON)を生成・ストリーミングする。
- OpenUI(仲介役): ストリーミングされてくる途中のデータ(未完成のJSON)や完成したデータをリアルタイムに解析し、UIコンポーネントの状態(Props)に落とし込む。
- フロントエンド(描画側):
OpenUIから渡されたコンポーネント名とPropsを元に、あらかじめ用意しておいたReactコンポーネント(例:
<ProgressBar/>や<Card/>)を動的にマッピングしてレンダリングする。
基本的な実装のイメージ
※手元で試す用のざっくりとしたコードメモ。
1. UIコンポーネントのレジストリ登録
生成したいUI(コンポーネント)と、その識別子をあらかじめマッピングしておく。
// 描画したい自作のコンポーネント群
import { WeatherCard } from '@/components/WeatherCard';
import { StockChart } from '@/components/StockChart';
// OpenUI側に「この名前が来たらこのコンポーネントを出す」と登録
export const uiRegistry = {
weather: WeatherCard,
stock: StockChart,
};
2. フロントエンドでのレンダリング
OpenUIが提供するコンポーネントやHooksを使うことで、ストリーミング中の「ローディング状態」や「徐々にUIが組み立てられていく様子」を最小限のコードで表現できる。
import { useGenerativeUI } from 'openui-framework-package'; // ※実際のパッケージ名に合わせる
import { uiRegistry } from './registry';
export default function ChatView() {
// AIからのストリームや出力をOpenUIのHookに流し込む
const { uiElements, submitMessage } = useGenerativeUI({
api: '/api/chat',
registry: uiRegistry,
});
return (
<div className="flex flex-col gap-4 p-4">
{uiElements.map((element, index) => {
// OpenUIがストリームを解析し、自動で適切なコンポーネントを割り当ててくれる
return <element.Component key={index} {...element.props} />;
})}
</div>
);
}
個人的な注目ポイントと使い所
💡 どんな時に使えそう?
AIエージェント系アプリ: ユーザーの指示に応じて、チャットUIの中に「フォーム」や「タスク一覧」「グラフ」を動的に差し込みたい時。
UXの向上: 完全にLLMの出力が終わるのを待つのではなく、ストリーミング中に「今まさにUIが生成されている感(骨組みから徐々にデータが埋まる挙動)」をユーザーに見せたい時。
次に触るときに深掘りしたい点
- コンポーネントのPropsの型定義(Zodなど)を、LLMのStructured Outputsのスキーマとどうやって一番スマートに同期させるか。
- ストリームが途中でエラー落ちした際のフォールバック処理の挙動。