Prompt input
The composer of an AI chat or agent panel. It grows with the text, shows attachment chips, hosts the Model selector and Context meter in its slots, and turns Send into Stop while the agent works. Focus draws the AI gradient around it.
Type / or @ to open a menu. Enter sends, Shift+Enter adds a line. The uploading file blocks Send until you remove it.
Add the source to a shadcn project
npx shadcn@latest add https://docs.atomus.io/r/ai-prompt-input.jsonOpen in v0Installs the component source, its CSS and the Atomus tokens into components/atomus/. How the registry works
States
Section titled “States”In Figma
Section titled “In Figma”From guidelines/components/ai-prompt-input.md
Properties, variants and anatomy of the Figma components. React props use the same names.
Prompt input
Section titled “Prompt input”The composer of an AI chat or agent panel: an auto-growing text area, attachment chips, a toolbar slot for the Model selector and tool toggles, and one button that switches between Send and Stop. Typing / or @ opens a command or mention menu. Part of the Agent kit.
Figma: component set Prompt input on the 🤖 Agent kit page of Atomus 4.0 (node 25553:1955), connected to React with Code Connect. Attachment chips: Prompt attachment (node 25553:1183). React props use the same names as the Figma properties below.
| Property | Type / options |
|---|---|
| Text | text |
| Placeholder | text |
| Size | md · lg |
| State | Ready · Submitted · Streaming · Error · Disabled |
| Attachments | slot (attachment chips) |
| Attach button | boolean (on) |
| Toolbar slot | slot (Model selector, toggles) |
| Actions slot | slot (Context meter, voice) |
| Command menu | boolean (off) |
| Disclaimer | text |
Anatomy
- Container:
bg-primary,border-primary, radius 2xl (3xl at lg). When focused, the border becomes--gradient-aiwith abg-ai-subtleglow. - Attachment chips: file icon or thumbnail, name, size or upload state, remove button.
- Text area: grows from
minRows(1) tomaxRows(8), then scrolls. - Bottom bar: Attach button and Toolbar slot on the left; Actions slot and Send / Stop on the right.
- Send / Stop: brand-solid arrow while ready;
fg-contrastsquare while the agent works. - Command menu: a listbox above the input, filtered by what follows
/or@. - Disclaimer: one centred line under the input.
Use for
- The main input of a chat, copilot side panel or agent workspace.
- Follow-up questions under an AI answer inside a card or drawer.
Not for
- Search fields: use Input with a search icon.
- Long-form writing without an AI reply: use a textarea.
- Forms with several fields: use Input, Select and the other form components.
Keyboard
- Enter sends; Shift+Enter adds a line. Enter never sends while the agent is working, and never during IME composition.
- In the command menu: ↑ ↓ move, Enter or Tab inserts, Esc closes.
- The Stop button is a normal button: Tab to it and press Enter or Space.
Accessibility
- The text area has a visually hidden label (
label, default “Message”); the placeholder is only a hint. - Send and Stop have accessible names (
submitLabel,stopLabel) and change name with the state. - The command menu uses
aria-activedescendant, so focus stays in the text area; a polite live region says how many suggestions there are. - “Generating response” is announced when the state turns busy.
- The disclaimer is linked with
aria-describedby.
import { PromptInput, ModelSelector, ContextMeter } from '@stanvision/atomus-react';
<PromptInput status={status} // 'ready' | 'submitted' | 'streaming' | 'error' onSubmit={(text, files) => send(text, files)} onStop={stop} attachments={files} onAttach={openFilePicker} onRemoveAttachment={removeFile} toolbar={<ModelSelector models={models} value={model} onChange={setModel} />} actions={<ContextMeter used={usage.tokens} limit={200000} />} triggers={[{ char: '/', items: commands }, { char: '@', label: 'People', items: people }]} disclaimer="AI can make mistakes. Check important information."/>Props at a glance
Section titled “Props at a glance”Full types and defaults are in React API at the end of the page.
status—submittedandstreamingshow Stop and block Enter; the text area stays editable.value/defaultValue/onChange— controlled or uncontrolled text;onSubmitgets the trimmed text and the attachments.attachments,onAttach,onRemoveAttachment— chips with an upload state; an uploading file blocks Send.toolbarandactions— slots on the left and right of the bottom bar.triggers—/and@menus with keyboard support (↑ ↓, Enter or Tab, Esc).size—mdfor panels,lgfor full-page chat.minRows/maxRowsset the growth.
Design guidance
Section titled “Design guidance”From the Figma guidelines.
Do
- Keep one Prompt input per conversation, docked at the bottom of the thread.
- Put the Model selector in the Toolbar slot and the Context meter in the Actions slot.
- Show Stop while the agent works and keep the text area editable, so people can draft the next message.
- Block Send while a file is still uploading; show the upload state on its chip.
- Add a one-line disclaimer when answers can be wrong.
Forbidden
- Never disable the text area while streaming — only Send is blocked.
- Never use the placeholder as the only label.
- Never put more than one primary action in the bar: Send is the primary action.
- Never move focus out of the text area when the reply arrives.
- Never send on Enter while a command menu is open — Enter inserts the highlighted item.
React API
Section titled “React API”Generated from react/src/components by scripts/gen-react-api.mjs — the same tables agents read in guidelines/components/ai-prompt-input.md.
Use only these props; anything else is not part of the API.
PromptInput
Section titled “PromptInput”Figma: Prompt input — the composer of an AI chat: auto-growing text, attachments, a toolbar slot, send ↔ stop, and “/” or “@” menus. Enter sends, Shift+Enter adds a line, Esc closes a menu.
import { PromptInput } from '@stanvision/atomus-react'; — source: react/src/components/ai/PromptInput.tsx
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
— | Figma: Text (controlled) |
defaultValue |
string |
'' |
Initial text when uncontrolled |
onChange |
(value: string) => void |
— | Called with the new value. |
onSubmit |
(text: string, attachments: PromptAttachment[]) => void |
— | Called with the trimmed text and the attachments on Enter or the send button |
onStop |
() => void |
— | Called by the stop button while status is submitted or streaming |
status |
'ready' | 'submitted' | 'streaming' | 'error' |
'ready' |
Figma: State — submitted and streaming turn Send into Stop and block new submits. |
placeholder |
string |
'Ask anything…' |
Figma: Placeholder — a hint, never the label |
label |
string |
'Message' |
Accessible name of the text area (visually hidden) |
attachments |
PromptAttachment[] |
[] |
Figma: Attachments — chips above the text |
onAttach |
() => void |
— | Figma: Attach button — shows the paperclip button; open your file picker here |
onRemoveAttachment |
(id: string) => void |
— | Called by a chip’s remove button |
toolbar |
ReactNode |
— | Figma: Toolbar slot — left of the bottom bar (ModelSelector, tool toggles) |
actions |
ReactNode |
— | Figma: Actions slot — right of the bottom bar, before Send (ContextMeter, voice) |
triggers |
PromptTrigger[] |
[] |
Figma: Command menu — “/” and “@” menus that open while typing |
disclaimer |
ReactNode |
— | Figma: Disclaimer — one line under the input (“AI can make mistakes. Check important info.”) |
minRows |
number |
1 |
Rows shown when empty |
maxRows |
number |
8 |
Rows before the text area scrolls |
size |
'md' | 'lg' |
'md' |
Figma: Size — md for side panels, lg for a full-page chat |
disabled |
boolean |
— | Disables the control. |
autoFocus |
boolean |
— | Focuses the text area on mount — only on pages whose main task is the chat |
submitLabel |
string |
'Send message' |
Accessible name of the send button |
stopLabel |
string |
'Stop generating' |
Accessible name of the stop button |
clearOnSubmit |
boolean |
true |
Clears the text after submit (uncontrolled only) |
className |
string |
— | Extra class on the root element (layout only — never restyle colours or sizes). |
Accepts only the props above.
PromptAttachment:
| Field | Type | Description |
|---|---|---|
id |
string |
Stable key |
name |
string |
File name shown on the chip |
kind? |
'file' | 'image' |
image shows previewUrl as a thumbnail |
size? |
string |
Human-readable size (“2.4 MB”) |
previewUrl? |
string |
Thumbnail for images |
status? |
'uploading' | 'ready' | 'error' |
uploading shows a spinner; error marks the chip |
PromptTrigger:
| Field | Type | Description |
|---|---|---|
char |
string |
The character that opens the menu: “/” for commands, “@” for mentions |
label? |
string |
Accessible name of the menu (“Commands”, “People”) |
items |
PromptCommand[] |
The menu items, filtered by what is typed after the character |
onSelect? |
(item: PromptCommand) => void |
Called when an item is chosen (after its text is inserted) |
PromptCommand:
| Field | Type | Description |
|---|---|---|
value |
string |
Inserted after the trigger character (“summarize” → “/summarize “) |
label |
string |
Text in the menu |
description? |
string |
One line under the label |
icon? |
ReactNode |
Leading icon (16px) |
insert? |
string |
Exact text to insert instead of trigger + value |