Prompt Bar
Collect a prompt, with optional sources, commands, attachments, and a model menu, and hand it to your application.
Overview
Prompt Bar is the field a person types into. On its own it is a text area that grows with its content and a Send button. Each extra is added by passing a prop: mentions for an @ list, commands for a / list, models for a model menu, and allowAttachments for files.
When the person sends, onSubmit receives the text, the files, and the selected model, and the bar clears. While a reply is in progress, pass onStop and Stop takes the place of Send. Prompt Bar does not call a model, upload a file, or cancel a request. What happens to the prompt, and what stopping means, are your application's decisions.
Anatomy
- SuggestionsThe list that opens above the field after @ or /
- AttachmentsOne chip for each chosen file, with a remove button
- InputA text area that grows to a limit, then scrolls
- ControlsThe row under the input
- AttachOpens the file picker, when attachments are allowed
- ModelA menu of models, when models are passed
- ActionsYour own controls, before Send
- SendSubmits the prompt, or stops the reply while one is in progress
PromptBar is one component. Installing prompt-bar brings Button and Menu along as registry dependencies.
Behavior
Sending
Enter sends. Shift + Enter adds a line. Send is disabled while there is no text and no file. While a person is composing text with an input method editor, Enter confirms the composition and does not send.
Suggestions
Typing @ or / at the start of a word opens the matching list. Typing more filters it: sources match anywhere in the label, commands match from the start. Choosing an item replaces what was typed with @label or /label and a space, and calls onItemSelect so your application can record what the text refers to.
Escape closes the list and leaves the text as typed. The list stays closed until the text changes.
Prompt Bar inserts text. It does not turn a mention into a token or run a command. Read the selection from onItemSelect, or parse the text you receive.
Attachments
With allowAttachments, the attach button opens the system file picker, and each chosen file appears as a chip that can be removed. Pass accept to limit the file types the picker offers. Prompt Bar does not check file size or type itself, and does not read or upload the files. onSubmit receives them as File objects.
Model
Passing models adds a menu. The first model is selected unless you pass defaultModel or control it with model and onModelChange. Each model has an id and a label, and can have a description shown beside it and an icon shown before it.
Stopping
Pass onStop while a reply is in progress, and leave it out the rest of the time. While it is passed, the Send button becomes Stop and calls your handler. Cancelling the request is your application's work, and so is leaving onStop out once the reply has ended.
Typing stays possible while Stop is shown, so the next prompt can be written during a reply. Enter does not send it until the reply has ended or been stopped. The draft is kept either way.
Stop and Send are one button, so focus does not move when a reply starts. After Stop is pressed, focus moves to the text area.
Disabled
disabled blocks typing, attaching, and sending, and dims the bar. Use it while the bar cannot be used at all, such as before a conversation has loaded, or during a reply that cannot be stopped. The typed text is kept. Stop still works while the bar is disabled.
Accessibility
The bar is a form, the input is a native text area named "Prompt", and Send is a submit button. Attach, Send, and each remove button have names. While onStop is passed, the same button is named "Stop". The model button is read as "Model: " followed by the selection.
When mentions or commands are passed, the text area stays a text box and points at the list with aria-controls. Focus stays in the text area while the list is open. The active option is exposed through aria-activedescendant, and a polite status region says how many suggestions there are, or that nothing matches.
Every string comes from labels.
This behavior has been checked with a keyboard. It has not been checked with a screen reader yet, and suggestions on a multi-line field are read differently across them. Test it with the screen readers your product supports before you rely on it.
Installation
Usage
The smallest bar takes one handler:
import { PromptBar } from "@/components/ai/prompt-bar"
<PromptBar placeholder="Write a message…" onSubmit={({ text }) => chat.send(text)} />Add onStop while a reply is in progress:
<PromptBar
placeholder="Write a message…"
onStop={chat.isStreaming ? () => chat.stop() : undefined}
onSubmit={({ text }) => chat.send(text)}
/>Add the extras your product has:
<PromptBar
mentions={[{ id: "invoices", label: "invoices", description: "Billing database" }]}
commands={[{ id: "summarize", label: "summarize", description: "Condense the thread so far" }]}
models={[{ id: "large", label: "Large" }, { id: "small", label: "Small" }]}
allowAttachments
accept="image/*,.pdf"
onItemSelect={(item, kind) => context.add(kind, item.id)}
onSubmit={({ text, attachments, model }) => chat.send({ text, attachments, model })}
/>Labels are matched against one typed word, so give each source and command a label without spaces.
Your own controls
actions places controls before Send. Use it for a dictation button that your application wires up, or for a Context Meter:
<PromptBar
onSubmit={send}
actions={
<Button variant="link" size="icon" aria-label="Start dictation" onClick={startDictation}>
<MicIcon aria-hidden="true" />
</Button>
}
/>Examples
Stop a reply
This preview has no model. A six-second timer stands in for a reply. Send a prompt, and Stop takes the place of Send until the timer ends or you press it.
Disabled while a reply is pending
This preview has no model. A two-second timer stands in for a reply, and the bar is disabled until it ends.
API reference
PromptBar
Accepts native form props beyond those listed.