レイアウトと動作を完全に制御できる、カスタムメッセージ入力 UI を構築します。Composer プリミティブは新しいメッセージを作成、または既存のメッセージを編集するためのインターフェイスです。
assistant-ui 入門 : プリミティブ – Composer
作成 : Masashi Okumura (@classcat.com)
作成日時 : 03/26/2026
バージョン : assistant-ui@0.0.85
* 本記事は assistant-ui.com/docs の以下のページを参考にしています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

assistant-ui 入門 : プリミティブ – Composer
レイアウトと動作を完全に制御できる、カスタムメッセージ入力 UI を構築します。
Composer プリミティブは新しいメッセージを作成、または既存のメッセージを編集するためのインターフェイスです。送信動作、キーボードショートカット、フォーカス管理、添付ファイルの状態、ストリーミング状態を処理します。
Preview

Code
import { ComposerPrimitive } from "@assistant-ui/react";
import { ArrowUpIcon } from "lucide-react";
export function MinimalComposer() {
return (
<ComposerPrimitive.Root className="flex w-full flex-col rounded-3xl border bg-muted">
<ComposerPrimitive.Input
placeholder="Ask anything..."
className="min-h-10 w-full resize-none bg-transparent px-5 pt-4 pb-3 text-sm focus:outline-none"
rows={1}
/>
<div className="flex items-center justify-end px-3 pb-3">
<ComposerPrimitive.Send className="flex size-8 items-center justify-center rounded-full bg-primary text-primary-foreground disabled:opacity-30">
<ArrowUpIcon className="size-4" />
</ComposerPrimitive.Send>
</div>
</ComposerPrimitive.Root>
);
}
クイックスタート
最小限の例:
import { ComposerPrimitive } from "@assistant-ui/react";
<ComposerPrimitive.Root>
<ComposerPrimitive.Input placeholder="Ask anything..." />
<ComposerPrimitive.Send>Send
</ComposerPrimitive.Root>
Root は <form> を、Input は <textarea> を、Send は <button> をレンダリングします。各パートはデフォルトでそれぞれのネイティブ要素をレンダリングし、要素置換 (element substitution) のための asChild をサポートしています。
ℹ️ ランタイム・セットアップ: プリミティブはランタイム・コンテキストを必要とします。UI をランタイム (例えば useLocalRuntime(…)) を持つ AssistantRuntimeProvider でラップします。See Pick a Runtime.
基本概念
新規メッセージ vs 編集モード
Thread 内に配置された Composer は新しいメッセージを作成します (compose)。Message 内に配置された Composer はそのメッセージを編集します。同じプリミティブが両方を処理し、動作はコンテキストに基づいて自動的に変化します。
// New message composer
<ThreadPrimitive.Root>
<ComposerPrimitive.Root>
<ComposerPrimitive.Input />
<ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
</ComposerPrimitive.Root>
</ThreadPrimitive.Root>
// Edit composer (inside a message)
<MessagePrimitive.Root>
<ComposerPrimitive.Root>
<ComposerPrimitive.Input />
<ComposerPrimitive.Send>Save</ComposerPrimitive.Send>
Cancel</ComposerPrimitive.Cancel>
</ComposerPrimitive.Root>
</MessagePrimitive.Root>
asChild パターン
すべてのプリミティブは asChild を受け取り、その動作を独自要素にマージします。これは、独自のデザインシステムでプリミティブを使用する方法です :
import { ComposerPrimitive } from "@assistant-ui/react";
<ComposerPrimitive.Input asChild>
<textarea
className="my-custom-textarea"
placeholder="Type here..."
/>
</ComposerPrimitive.Input>
<ComposerPrimitive.Send asChild>
<MyButton variant="primary">Send
</ComposerPrimitive.Send>
プリミティブの動作 (キーボード処理、無効な状態、フォーム送信) は独自の要素にマージされます。独自スタイル、独自コンポーネント、プリミティブ配線となります。
部品 (パーツ)
Root
メッセージ作成用のフォーム・コンテナ。asChild が設定されていない場合は、<form> 要素をレンダリングします。
<ComposerPrimitive.Root className="flex w-full flex-col rounded-3xl border bg-muted">
<ComposerPrimitive.Input placeholder="Ask anything..." />
<ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
</ComposerPrimitive.Root>
Input
キーボードショートカットを備えたテキスト入力。asChild が設定されていない場合は、<textarea> 要素をレンダリングします。
<ComposerPrimitive.Input
submitMode="ctrlEnter"
cancelOnEscape
placeholder="Ask anything..."
/>
Props
- submitMode? “none” | “enter” | “ctrlEnter”
- cancelOnEscape? : boolean – Esc が押されたとき、メッセージ作成をキャンセルするかどうか。
Send
composer フォームを送信します。asChild が設定されていない場合は、<button> 要素をレンダリングします。
<ComposerPrimitive.Cancel className="rounded-md px-3 py-2 text-sm hover:bg-muted">
Cancel
</ComposerPrimitive.Cancel>
Cancel
現在の作成や編集セッションをキャンセルします。asChild が設定されていない場合は、<button> 要素をレンダリングします。
<ComposerPrimitive.Cancel className="rounded-md px-3 py-2 text-sm hover:bg-muted">
Cancel
</ComposerPrimitive.Cancel>
以上