Composer
A prompt shell on input-group with mode and mic slots shaped for AI SDK useChat.
Built on shadcn Input Group. Distributed as a shadcn registry block from GitHub.
Composer
Multi-line prompt with attach, mode slot, mic slot, and circular ↑ send.
Composer
Installation
npx shadcn@latest add layishsieger/registry/composerThe CLI copies the block into your project. You own the source.
Usage
import { useState } from "react"
import { Composer } from "@/components/composer"
export function Example() {
const [text, setText] = useState("")
// const { sendMessage, status, stop } = useChat(...)
return (
<Composer
value={text}
onValueChange={setText}
status="ready"
onStop={() => {
// stop()
}}
onSubmit={({ text, files }) => {
// sendMessage({ text, files })
setText("")
}}
modeSlot={<button type="button">Ask</button>}
/>
)
}Host owns transport. Wire onSubmit, status, and onStop from useChat. The block does not import @ai-sdk/react.
Enter key
Default Enter sends. Shift+Enter inserts a newline. Set enterKeyBehavior="focus-send" so the first Enter focuses send and the second Enter submits. After submit, focus returns to the field so Enter does not immediately hit Stop.
Enter focuses send
Keyboard
| Shortcut | Action |
|---|---|
| CtrlA | Attach |
| CtrlO | Cycle mode + expand chips |
| CtrlD | Dictation / mic |
| /Ctrl | Send (or stop when busy) |
| Newline |
On desktop (fine pointer), modifier is ⌘ on macOS and Ctrl elsewhere — both currently match as Mod. Shortcuts only run while focus is inside the composer. On touch / coarse pointers, Mod shortcuts and Kbd tooltips are off and Enter inserts a newline — send with the button. Tooltips dismiss on click.
shortcuts={false} turns off Mod action bindings (attach / mode / dictation) and tooltips. It does not disable field Enter behavior: still inserts a newline, and / Ctrl still send or stop while busy (on fine pointers). Set shortcutTooltips={false} to keep Mod bindings without hover hints.
shortcuts off
No Mod action bindings or Kbd tooltips. Enter / Shift+Enter / Mod+Enter send behavior still applies on desktop.
shortcuts={false}
tooltips off
Bindings stay; hover Kbd tooltips hidden.
shortcutTooltips={false}
SpeechInput
The registry block keeps micSlot only — no AI Elements dependency. Install SpeechInput from AI Elements when you want voice. In Chrome/Edge it uses the browser Web Speech API — free, no Layish tokens or API keys. Optional onAudioRecorded (Whisper/etc.) is host-paid if you wire it for Firefox/Safari.
npx ai-elements@latest add speech-inputimport { useState } from "react"
import { Composer } from "@/components/composer"
import { SpeechInput } from "@/components/ai-elements/speech-input"
export function Example() {
const [text, setText] = useState("")
return (
<Composer
value={text}
onValueChange={setText}
micSlot={({ disabled }) => (
<SpeechInput
size="icon-sm"
variant="ghost"
disabled={disabled}
onTranscriptionChange={(transcript) => {
const next = transcript.trim()
if (!next) return
setText((current) => {
const base = current.trim()
return base ? `${base} ${next}` : next
})
}}
/>
)}
/>
)
}SpeechInput in micSlot
Appends final transcripts into the composer value.
SpeechInput in micSlot
Uses the browser Web Speech API in Chrome/Edge — free, no API keys or tokens from Layish. Optional Whisper via onAudioRecorded is host-paid if you add it.
AI SDK
Map sendMessage, status, and stop. Busy status flips the primary control to stop.
// Wired like useChat — submit → sendMessage, busy → stop()
API
Composer
| Prop | Type | Default |
|---|---|---|
| value / onValueChange | controlled text | — |
| onSubmit | (message) => void | — |
| status | ready | submitted | streaming | error | ready |
| onStop | () => void | — |
| enterKeyBehavior | submit | focus-send | submit |
| shortcuts | boolean | true |
| shortcutTooltips | boolean | true |
| modeSlot / micSlot / attachSlot | ReactNode | render prop | mic + attach defaults |
| className / disabled | layout / disable | — |