Components / AI
AIComposer
Production composer with attachments, modes, plugins, context badges, and send states.
Preview
1"use client"2
3import * as React from "react"4import { AIComposer, type AIComposerMode } from "@/components/ui/ai-composer"5
6export function AIComposerDemo() {7 const [value, setValue] = React.useState("")8 const [mode, setMode] = React.useState<AIComposerMode>("ask")9
10 return (11 <AIComposer12 value={value}13 onChange={setValue}14 onSend={async (message, files) => {15 console.log({ message, files })16 }}17 mode={mode}18 onModeChange={setMode}19 layout="landing"20 21 plugins={[22 { id: "github", label: "GitHub", description: "qentrah/DUI" }23 ]}24 />25 )26}thread
The thread style of this component.
1"use client"2
3import * as React from "react"4import { AIComposer, type AIComposerMode } from "@/components/ui/ai-composer"5
6export function AIComposerDemo() {7 const [value, setValue] = React.useState("")8 const [mode, setMode] = React.useState<AIComposerMode>("ask")9
10 return (11 <AIComposer12 value={value}13 onChange={setValue}14 onSend={async (message, files) => {15 console.log({ message, files })16 }}17 mode={mode}18 onModeChange={setMode}19 layout="thread"20 21 plugins={[22 { id: "github", label: "GitHub", description: "qentrah/DUI" }23 ]}24 />25 )26}sending
The sending style of this component.
1"use client"2
3import * as React from "react"4import { AIComposer, type AIComposerMode } from "@/components/ui/ai-composer"5
6export function AIComposerDemo() {7 const [value, setValue] = React.useState("")8 const [mode, setMode] = React.useState<AIComposerMode>("ask")9
10 return (11 <AIComposer12 value={value}13 onChange={setValue}14 onSend={async (message, files) => {15 console.log({ message, files })16 }}17 mode={mode}18 onModeChange={setMode}19 layout="thread"20 sending21 onStop={() => console.log("stop")}22 plugins={[23 { id: "github", label: "GitHub", description: "qentrah/DUI" }24 ]}25 />26 )27}Installation
Add the source directly to your project with the shadcn CLI.
npx shadcn@latest add qentrah/DUI/ai-composerConnect it to your product
AIComposer owns presentation and local attachment previews. Your application owns message delivery, cancellation, plugin state, and voice capture through callbacks. This keeps the installed component independent from any backend or AI SDK.
assistant-composer.tsx
1"use client"2
3import * as React from "react"4import {5 AIComposer,6 type AIComposerMode,7} from "@/components/ui/ai-composer"8
9export function AssistantComposer() {10 const [value, setValue] = React.useState("")11 const [mode, setMode] = React.useState<AIComposerMode>("ask")12 const [sending, setSending] = React.useState(false)13
14 async function send(message: string, files: File[]) {15 setSending(true)16 try {17 await yourApi.send({ message, files, mode })18 } finally {19 setSending(false)20 }21 }22
23 return (24 <AIComposer25 value={value}26 onChange={setValue}27 onSend={send}28 onStop={() => yourApi.stop()}29 sending={sending}30 mode={mode}31 onModeChange={setMode}32 layout="thread"33 />34 )35}Plugins, context, and voice
Plugin options and badges are plain data. The selector reports user intent through onPluginToggle; it never connects accounts or sends data by itself.
composer-integrations.tsx
1<AIComposer2 value={value}3 onChange={setValue}4 onSend={send}5 mode={mode}6 onModeChange={setMode}7 badges={[8 { id: "website", label: "Website", tone: "info" },9 ]}10 plugins={[11 { id: "github", label: "GitHub", description: "qentrah/DUI" },12 { id: "sheets", label: "Google Sheets", description: "Planning" },13 ]}14 selectedPluginIds={selectedPluginIds}15 onPluginToggle={togglePlugin}16 onVoiceClick={startVoiceInput}17/>18
Behavior contract
- Enter sends; Shift + Enter creates a new line.
- Files can be selected or dropped, are deduplicated, and respect
maxAttachments. - The component clears text and attachments only after
onSendresolves successfully. - Use
sendingwithonStopto expose cancellation.
Usage
Install the component, import it from your configured UI directory, and adapt the source to your product. Open the source panel under any example to copy that exact variant.