OpenUI とは
OpenUI は フルスタック Generative UI フレームワークです。LLM の出力をテキストとしてのみ扱うのではなく、コンポーネントを定義し、モデルがストリームしながら構造化 UI を生成・描画できるようにします。
中心にあるのは OpenUI Lang — コンパクトでストリーミングに最適化された UI 生成言語。JSON-based なフォーマットより大幅にトークン効率が高く、React の <Renderer> がトークンの到着と同時に UI を段階的に描画します。
67%
最大トークン削減(vs JSON)
4
公開パッケージ
30+
組み込みコンポーネント
3
チャットレイアウト
主な機能
- OpenUI Lang — モデル出力向けのコンパクトなストリーミング UI 言語
- 組み込みコンポーネントライブラリ — チャート・フォーム・テーブル・レイアウト等がすぐ使える
- コンポーネントライブラリからのプロンプト生成 — 定義したコンポーネントから自動でシステムプロンプトを生成
- ストリーミングレンダラー — トークン到着と同時に React コンポーネントを逐次描画
- チャット & アプリサーフェス — アシスタント・コパイロット・インタラクティブ UI まで同じ基盤で対応
パイプライン全体像
コンポーネントライブラリが「モデルが生成できるもの」を定義します。そこからシステムプロンプトを生成し、LLM に渡すと OpenUI Lang のストリームが返り、Renderer がリアルタイムで UI を描画します。
flowchart LR
A["🗂️ Component
Library"]:::amber --> B["📝 System
Prompt"]:::step B --> C["🤖 LLM"]:::step C --> D["📡 OpenUI Lang
Stream"]:::amber D --> E["⚙️ Renderer"]:::step E --> F["🖼️ Live UI"]:::step classDef amber fill:#fef3c7,stroke:#d97706,color:#92400e classDef step fill:#f9fafb,stroke:#d1d5db,color:#374151
Library"]:::amber --> B["📝 System
Prompt"]:::step B --> C["🤖 LLM"]:::step C --> D["📡 OpenUI Lang
Stream"]:::amber D --> E["⚙️ Renderer"]:::step E --> F["🖼️ Live UI"]:::step classDef amber fill:#fef3c7,stroke:#d97706,color:#92400e classDef step fill:#f9fafb,stroke:#d1d5db,color:#374151
ステップ解説
| ステップ | 担当 | 説明 |
|---|---|---|
| 1. コンポーネント定義 | defineComponent | 名前・Zod スキーマ・React レンダラーでコンポーネントを宣言 |
| 2. ライブラリ作成 | createLibrary | コンポーネントをまとめてライブラリ化 |
| 3. プロンプト生成 | library.prompt() | 許可コンポーネント一覧をモデルへ伝えるシステムプロンプトを自動生成 |
| 4. LLM 呼び出し | バックエンド API | ChatProvider / processMessage でストリーミングリクエストを実行 |
| 5. ストリーム受信 | Streaming Adapter | OpenAI SSE / AG-UI などを内部イベント形式に正規化 |
| 6. レンダリング | <Renderer> | OpenUI Lang テキストをリアルタイムで React コンポーネントに変換 |
レイヤー構成
graph TB
subgraph UI["@openuidev/react-ui"]
FS["FullScreen"]
CP["Copilot"]
BT["BottomTray"]
COMP["30+ Components"]
end
subgraph HEADLESS["@openuidev/react-headless"]
PROV["ChatProvider"]
HOOKS["useThread / useThreadList"]
ADAPT["Streaming Adapters"]
end
subgraph LANG["@openuidev/react-lang"]
DC["defineComponent"]
CL["createLibrary"]
REN["<Renderer>"]
PARSER["Parser / StreamingParser"]
end
subgraph CLI["@openuidev/cli"]
CREATE["openui create"]
GEN["openui generate"]
end
UI --> HEADLESS
UI --> LANG
HEADLESS --> LANG
各パッケージは独立してインストール可能です。
@openuidev/react-ui はフルスタックで導入でき、@openuidev/react-lang だけを使って独自 UI に組み込むことも可能です。
4 パッケージの役割
@openuidev/react-lang
コアランタイム
コンポーネント定義・プロンプト生成・Renderer・パーサーを提供。OpenUI の心臓部。
peer: react ≥19
@openuidev/react-headless
ヘッドレスチャット状態管理
ChatProvider・useThread・useThreadList・Streaming Adapters を提供。UI は持たないため、任意のデザインに組み合わせ可能。
peer: react ≥19, zustand ^4.5
@openuidev/react-ui
UI コンポーネント & レイアウト
FullScreen / Copilot / BottomTray のチャットレイアウト、30+ の UI コンポーネント、openuiLibrary を同梱。
peer: react-lang, react-headless
@openuidev/cli
CLI ツール
create でアプリのスキャフォールド、generate でシステムプロンプト or JSON Schema を生成。npx @openuidev/cli@latest
リポジトリ構成
openui/ ├── packages/ │ ├── react-lang/ # コアランタイム(パーサー・レンダラー・プロンプト生成) │ ├── react-headless/ # ヘッドレスチャット状態 & ストリーミングアダプター │ ├── react-ui/ # 組み込みチャットレイアウト & コンポーネントライブラリ │ └── openui-cli/ # スキャフォールド & プロンプト生成 CLI ├── skills/ │ └── openui/ # AI コーディングアシスタント向け Agent Skill ├── examples/ │ └── openui-chat/ # 動作サンプル(Next.js) ├── docs/ # openui.com ドキュメントサイト └── benchmarks/ # トークン効率ベンチマーク
CLI でアプリを作成(最速)
npx @openuidev/cli@latest create --name genui-chat-app cd genui-chat-app echo "OPENAI_API_KEY=sk-your-key-here" > .env npm run dev
これだけで OpenUI Lang・ライブラリ駆動プロンプト・ストリーミング対応のチャットアプリが起動します。
FullScreen レイアウトをそのまま使う
import { FullScreen } from "@openuidev/react-ui";
import "@openuidev/react-ui/components.css";
function App() {
return (
<FullScreen
apiUrl="/api/chat"
threadApiUrl="/api/threads"
/>
);
}
独自コンポーネントを定義してレンダリング
① コンポーネントを定義
import { defineComponent } from "@openuidev/react-lang";
import { z } from "zod";
const Greeting = defineComponent({
name: "Greeting",
description: "挨拶メッセージを表示",
props: z.object({
name: z.string().describe("対象の名前"),
mood: z.enum(["happy", "excited"]).optional(),
}),
component: ({ name, mood }) => (
<div className={mood === "excited" ? "text-xl font-bold" : ""}>
こんにちは、{name}!
</div>
),
});② ライブラリを作成しプロンプトを生成
import { createLibrary } from "@openuidev/react-lang";
const library = createLibrary({ components: [Greeting, Card, Table] });
// モデルへ渡すシステムプロンプト
const systemPrompt = library.prompt({
preamble: "あなたは親切なアシスタントです。",
examples: ["<Greeting name='Alice' mood='happy' />"],
});③ ストリーミング出力を Renderer で描画
import { Renderer } from "@openuidev/react-lang";
function AssistantMessage({ response, isStreaming }) {
return (
<Renderer
response={response}
library={library}
isStreaming={isStreaming}
onAction={(event) => console.log("Action:", event)}
/>
);
}
ChatProvider でチャット状態管理
import { ChatProvider } from "@openuidev/react-headless";
import { openAIAdapter } from "@openuidev/react-headless";
function App() {
return (
<ChatProvider
apiUrl="/api/chat"
streamProtocol={openAIAdapter} // OpenAI SDK のストリームに対応
>
<YourChatUI />
</ChatProvider>
);
}
// チャット状態の参照
import { useThread } from "@openuidev/react-headless";
function ChatMessages() {
const { messages, isRunning, processMessage } = useThread();
// ...
}
react-lang — コア API
| エクスポート | 種別 | 説明 |
|---|---|---|
defineComponent(config) | 関数 | 名前・Zod スキーマ・React レンダラーでコンポーネントを宣言 |
createLibrary(def) | 関数 | コンポーネント配列からライブラリを生成 |
library.prompt(opts) | メソッド | システムプロンプトを生成 |
library.toJSONSchema() | メソッド | JSON Schema 表現を返す |
<Renderer> | コンポーネント | OpenUI Lang テキストをリアルタイムで React に変換 |
createParser(library) | 関数 | 完成テキスト用ワンショットパーサー |
createStreamingParser(library) | 関数 | ストリーミング入力向けインクリメンタルパーサー |
Renderer Props
| Prop | 型 | 説明 |
|---|---|---|
response | string | null | モデルからの生 OpenUI Lang テキスト |
library | Library | createLibrary() で作成したライブラリ |
isStreaming | boolean | ストリーミング中かどうか(フォームを無効化) |
onAction | (event) => void | コンポーネントのアクション発火時コールバック |
onStateUpdate | (state) => void | フォームフィールド値変更時コールバック |
initialState | Record<string, any> | フォームの初期状態(ハイドレーション用) |
Context Hooks(コンポーネントレンダラー内で使用)
| Hook | 説明 |
|---|---|
useIsStreaming() | ストリーミング中かどうか |
useTriggerAction() | アクションイベントを発火 |
useGetFieldValue() | フォームフィールドの現在値を取得 |
useSetFieldValue() | フォームフィールドの値を設定 |
useFormName() | 現在のフォーム名を取得 |
useRenderNode() | 子要素ノードを描画 |
react-headless — チャット状態 API
| Hook / コンポーネント | 返り値 / 説明 |
|---|---|
<ChatProvider> | スレッド・メッセージ・ストリーミング状態を管理する Context プロバイダー |
useThread() | messages, isRunning, processMessage, cancelMessage, appendMessages など |
useThreadList() | threads, selectedThreadId, selectThread, switchToNewThread, deleteThread など |
useMessage() | メッセージコンポーネント内で現在のメッセージにアクセス |
Streaming Adapters
| アダプター | 説明 |
|---|---|
agUIAdapter | デフォルト — AG-UI SSE イベントをパース |
openAIAdapter | OpenAI Chat Completions ストリーミングをパース |
openAIResponsesAdapter | OpenAI Responses API ストリームをパース |
openAIReadableStreamAdapter | OpenAI SDK の Stream.toReadableStream() NDJSON 出力をパース |
カスタムアダプターは
StreamProtocolAdapter インターフェースを実装し、AsyncIterable<AGUIEvent> を yield するだけで作成できます。
react-ui — チャットレイアウト & テーマ
| コンポーネント / エクスポート | 説明 |
|---|---|
FullScreen | スレッドサイドバー付きフルページチャット |
Copilot | サイドパネル型コパイロットオーバーレイ |
BottomTray | 折りたたみ可能なボトムトレイチャット |
openuiLibrary | フルコンポーネントライブラリ(チャート・テーブル・フォーム等) |
openuiChatLibrary | チャット最適化サブセット(フォローアップ・ステップ・コールアウト) |
ThemeProvider | カラー・タイポグラフィ・スペーシングのテーマ管理 |
createTheme(overrides) | デフォルトテーマのカスタマイズ |
コンポーネント一覧(カテゴリ別)
| カテゴリ | コンポーネント |
|---|---|
| レイアウト | Card, CardHeader, SectionBlock, Tabs, Accordion, Carousel, Separator, Steps |
| データ表示 | Table, Charts(バー/ライン/エリア/パイ/レーダー/散布図), ListBlock, Tag, CodeBlock, Image, ImageGallery |
| フォーム | Input, TextArea, Select, CheckBoxGroup, RadioGroup, SwitchGroup, Slider, DatePicker, FormControl |
| アクション | Button, Buttons, IconButton, FollowUpBlock, FollowUpItem |
| フィードバック | Callout, TextCallout, MessageLoading |
| コンテンツ | TextContent, MarkDownRenderer |
CLI コマンド
| コマンド | 説明 | 主なオプション |
|---|---|---|
openui create |
Next.js + OpenUI Chat のアプリをスキャフォールド | --name, --no-interactive |
openui generate <entry> |
createLibrary() エクスポートからシステムプロンプト or JSON Schema を生成 |
--json-schema, --out, --export |
# システムプロンプトを標準出力へ npx @openuidev/cli generate ./src/library.ts # JSON Schema をファイルへ書き出し npx @openuidev/cli generate ./src/library.ts --json-schema --out ./artifacts/schema.json
トークン効率の比較
tiktoken(GPT-5 エンコーダー)で計測。7 つの UI シナリオで JSON ベースの 2 フォーマットと比較。
| シナリオ | Vercel JSON-Render | Thesys C1 JSON | OpenUI Lang | vs Vercel | vs C1 |
|---|---|---|---|---|---|
| simple-table | 340 | 357 | 148 | -56.5% | -58.5% |
| chart-with-data | 520 | 516 | 231 | -55.6% | -55.2% |
| contact-form | 893 | 849 | 294 | -67.1% | -65.4% |
| dashboard | 2247 | 2261 | 1226 | -45.4% | -45.8% |
| pricing-page | 2487 | 2379 | 1195 | -52.0% | -49.8% |
| settings-panel | 1244 | 1205 | 540 | -56.6% | -55.2% |
| e-commerce-product | 2449 | 2381 | 1166 | -52.4% | -51.0% |
| 合計 | 10180 | 9948 | 4800 | -52.8% | -51.7% |
計測方法: tiktoken GPT-5 エンコーダー使用。各シナリオのトークン数は完全なシステムプロンプト込みで計測。詳細な再現手順は
benchmarks/ ディレクトリに記載。
なぜトークン効率が重要か
- コスト削減 — トークン数が半分以下なら API コストも大幅に削減
- レスポンス速度 — 出力量が少ないほど最初のトークンが届くまでの時間(TTFT)が短縮
- コンテキスト節約 — 会話履歴と UI 出力を同じコンテキストウィンドウ内に収めやすい
- 精度向上 — 簡潔な構造はモデルが誤った JSON を生成するリスクを低減
参考リンク
- openui.com — 公式ドキュメント・Playground
- GitHub: thesysdev/openui — ソースコード
- npm: @openuidev/react-lang — コアランタイム
- Discord コミュニティ — 質問・情報交換
- benchmarks/ — トークン効率計測の詳細