A composable AI prompt composer with attachments, toolbar actions, send states, queued prompts, inline tokens, and drag-and-drop support.
Use PromptInput as the message composer for chat and agent interfaces.
Use the secondary variant when the composer sits on a default surface.
Use the inline layout when attachment, input, and send controls should stay aligned in one row while the textarea autosizes as users type or add line breaks.
Use the compact layout when the composer should start as a single-row pill and switch to the stacked composer once text wraps or attachments are added.
Compose PromptInput with workflow controls to build compact agent surfaces such as review follow-ups and commit actions.
Compose PromptInput with PromptSuggestion to create a landing-style composer with quick-start prompts.
Prompt input supports submitted, streaming, ready, and error states for send/stop behavior.
Use PromptInput.Queue to show queued follow-up prompts in a compact card above the composer shell. Place it as a sibling of PromptInput.Shell inside PromptInput. Keep queue rows text-only for a clean surface — store attachments in your queue model and restore them to PromptInput.Attachments when the user edits a queued prompt.
Use PromptInput.TokenInput instead of PromptInput.TextArea when the prompt should carry inline tokens such as @mentions or /commands. It is built on the React Aria Components TokenField, so its value is a TokenFieldValue segment list rather than a string, and it renders a contenteditable element instead of a textarea.
Drive it with usePromptInputTokens, which tracks the value and resolves the trigger the caret currently sits in. Wrap both the field and PromptInput.TokenSuggestions in PromptInput.Tokens so arrow keys and Enter move through the menu while the caret stays in the field. The query ends at the first space unless you pass allowsSpaces, and the menu closes when the caret moves out of the trigger or the field loses focus. Escape dismisses the menu for the current trigger until the caret leaves it; every other close, including close(), only hides the menu until the next edit or caret move.
The suggestion menu is anchored to the caret, not to the composer. For a composer docked to the bottom of the viewport, pass placement="top start" so the menu opens upward instead of covering the toolbar.
PromptInput.Tokens is a React Aria Autocomplete, so every menu and list box rendered inside it
joins the suggestion collection. Keep PromptInput.Toolbar and any other menus outside it.
TokenField is an alpha React Aria Components API. Its behavior and props may change in a future
release.
The token field owns the prompt and flattens it into a string for PromptInput.Send and the compact layout. Leave value on PromptInput unset when you use it — clear the prompt with tokens.setValue(new TokenFieldValue([])) instead.
Pair PromptInput with ChatAttachmentInput.Dropzone to accept dropped files. While a file is dragged over the shell, the composer shows a dotted accent border.
import {PromptInput} from "@heroui-pro/react";
<PromptInput value={value} layout="stacked" onSubmit={send} onValueChange={setValue}>
<PromptInput.Queue>
<PromptInput.Queue.List values={queuedPrompts} onReorder={setQueuedPrompts}>
{queuedPrompts.map((prompt) => (
<PromptInput.Queue.Item key={prompt.id} value={prompt}>
<PromptInput.Queue.Item.Handle />
<PromptInput.Queue.Item.Body>
<PromptInput.Queue.Item.Icon />
<PromptInput.Queue.Item.Content>Queued prompt text</PromptInput.Queue.Item.Content>
</PromptInput.Queue.Item.Body>
<PromptInput.Queue.Item.Actions>
<PromptInput.Queue.Item.Remove />
<PromptInput.Queue.Item.More />
</PromptInput.Queue.Item.Actions>
</PromptInput.Queue.Item>
))}
</PromptInput.Queue.List>
</PromptInput.Queue>
<PromptInput.Shell>
<PromptInput.Content>
<PromptInput.TextArea placeholder="What do you want to know?" />
</PromptInput.Content>
<PromptInput.Toolbar>
<PromptInput.ToolbarStart>
<PromptInput.Action aria-label="Attach file">...</PromptInput.Action>
</PromptInput.ToolbarStart>
<PromptInput.ToolbarEnd>
<PromptInput.Send />
</PromptInput.ToolbarEnd>
</PromptInput.Toolbar>
</PromptInput.Shell>
<PromptInput.Footer>AI can make mistakes. Check important info.</PromptInput.Footer>
</PromptInput>;To use inline tokens, swap PromptInput.TextArea for PromptInput.TokenInput and add a suggestion menu:
import {Menu} from "@heroui/react";
import {PromptInput, TokenFieldValue, usePromptInputTokens} from "@heroui-pro/react";
const users = [
{id: "ada", name: "Ada Lovelace", username: "ada"},
{id: "grace", name: "Grace Hopper", username: "grace"},
];
function Composer() {
const tokens = usePromptInputTokens({triggers: ["@"]});
const items = users.filter((user) => user.username.startsWith(tokens.query));
return (
<PromptInput onSubmit={() => tokens.setValue(new TokenFieldValue([]))}>
<PromptInput.Shell>
<PromptInput.Tokens>
<PromptInput.Content>
<PromptInput.TokenInput tokens={tokens} />
</PromptInput.Content>
<PromptInput.TokenSuggestions tokens={tokens}>
<Menu items={items}>
{(item) => (
<Menu.Item id={item.id} onAction={() => tokens.insertToken(`@${item.username}`)}>
{item.name}
</Menu.Item>
)}
</Menu>
</PromptInput.TokenSuggestions>
</PromptInput.Tokens>
<PromptInput.Toolbar>
<PromptInput.ToolbarEnd>
<PromptInput.Send />
</PromptInput.ToolbarEnd>
</PromptInput.Toolbar>
</PromptInput.Shell>
</PromptInput>
);
}.prompt-input - Root state wrapper.prompt-input__shell - Composer surface.prompt-input__content - Textarea and attachment content area.prompt-input__attachments - Attachment preview row.prompt-input__textarea - Text area.prompt-input__token-input - Contenteditable token field.prompt-input__token - Inline token pill.prompt-input__token-suggestions - Caret-anchored suggestion popover.prompt-input__toolbar - Absolute toolbar row.prompt-input__toolbar-start - Leading toolbar actions.prompt-input__toolbar-end - Trailing toolbar actions.prompt-input__footer - Disclaimer/footer text.prompt-input__send - Send/stop button.prompt-input__queue - Queued prompts container.prompt-input__queue-list - Scrollable queue list.prompt-input__queue-item - Single queued prompt row.prompt-input__queue-item-handle - Drag handle (presentational).prompt-input__queue-item-icon - Leading queue item icon.prompt-input__queue-item-body - Main queue row content stack.prompt-input__queue-item-content - Clamped prompt text (2 lines).prompt-input__queue-item-description - Secondary queue item text.prompt-input__queue-item-actions - Trailing row actions.prompt-input__queue-item-attachments - Attachment preview row inside a queue item.prompt-input__queue-item-attachments-overflow - Hidden attachment count labelThe root also sets --prompt-input-max-height from maxHeight, which caps .prompt-input__token-input.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | internal | Controlled input value |
onValueChange | (value: string) => void | - | Called when the text changes |
onSubmit | () => void | - | Called when submit is requested |
onStop | () => void | - | Called by the send button in stoppable states |
status | 'ready' | 'submitted' | 'streaming' | 'error' | 'ready' | Composer run state |
isDisabled | boolean | false | Disable composer controls |
lockInputOnRun | boolean | true | Disable textarea while submitted/streaming |
maxHeight | number | string | 240 | Max input height, also as a CSS variable |
size | 'sm' | 'md' | 'lg' | 'md' | Composer size |
variant | 'primary' | 'secondary' | 'primary' | Surface styling variant |
layout | 'stacked' | 'compact' | 'inline' | 'stacked' | Composer layout and toolbar placement |
Extends HeroUI TextArea props.
| Prop | Type | Default | Description |
|---|---|---|---|
disableAutosize | boolean | false | Disable automatic height adjustment |
An alternative to PromptInput.TextArea that renders inline tokens. Extends React Aria Components TokenInput props. Enter submits and Shift+Enter inserts a newline, and Enter is ignored while the composer is running unless allowSubmitWhileRunning is set, matching PromptInput.TextArea.
| Prop | Type | Default | Description |
|---|---|---|---|
tokens | PromptInputTokenState | - | Required token state from usePromptInputTokens |
children | (segment: TokenSegment) => ReactElement | PromptInput.Token | Renders each token segment |
placeholder | string | 'What do you want to know?' | Text shown while the field is empty |
allowsNewlines | boolean | true | Whether Shift+Enter inserts a newline |
isReadOnly | boolean | false | Make the field read only |
isDisabled | boolean | inherited | Defaults to the composer's disabled and run-lock state |
Connects PromptInput.TokenInput to PromptInput.TokenSuggestions so arrow keys and Enter move through the menu while the caret stays in the field. Wrap both with it, and nothing else — it is a React Aria Autocomplete, whose props it extends, so any other menu inside it joins the suggestion collection. Renders no DOM of its own.
A suggestion menu anchored to the caret of the active trigger. Extends React Aria Components Popover props except triggerRef and getTargetRect, which it sets from the token state.
| Prop | Type | Default | Description |
|---|---|---|---|
tokens | PromptInputTokenState | - | Required token state from usePromptInputTokens |
isOpen | boolean | tokens.isOpen | Overrides open state |
placement | Placement | 'bottom start' | Placement relative to the caret |
The popover hides itself while its menu has no items, so narrowing isOpen is optional.
A non-editable inline token. Extends React Aria Components Token props. Exposes [data-selected] and [data-disabled] for styling.
Tracks a token field value and resolves the trigger the caret currently sits in.
| Option | Type | Default | Description |
|---|---|---|---|
triggers | string[] | ['@', '/'] | Strings that open the menu at a word boundary |
allowsSpaces | boolean | false | Whether the query may run past the first space |
defaultValue | TokenFieldValue | empty | Initial value when uncontrolled |
value | TokenFieldValue | - | Controlled value |
onChange | (value: TokenFieldValue) => void | - | Called whenever the value changes |
It returns value, setValue, inputRef, anchor, query, trigger, isOpen, insertToken(text, {appendSpace, value}), and close(). close() hides the menu until the next edit or caret move, while Escape keeps it closed for the current trigger. insertToken and close keep a stable identity, so menu items can capture them in onAction without a dependencies list.
Extends HeroUI Button props.
| Prop | Type | Default | Description |
|---|---|---|---|
tooltip | ReactNode | - | Optional tooltip content |
Extends HeroUI Button props. Uses the current prompt status to render send, loading, stop, or error icons.
Layout slots for composing the prompt input. Each slot supports the corresponding native element props.
| Prop | Type | Default | Description |
|---|---|---|---|
actionsVisibility | 'always' | 'hover' | 'hover' | When queue row actions are visible |
| Prop | Type | Default | Description |
|---|---|---|---|
values | T[] | - | Controlled queue values. Pass with onReorder to enable Motion reordering. |
onReorder | (values: T[]) => void | - | Called when items are reordered via drag and drop. |
axis | 'x' | 'y' | 'y' | Reorder axis when drag and drop is enabled. |
| Prop | Type | Default | Description |
|---|---|---|---|
value | T | - | Item value from values. Required when reordering is enabled. |
Presentational slots for queue row structure. Wrap Icon and Content in Body so the icon sits beside the clamped text and stays vertically centered. Content clamps text to two lines with an ellipsis.
Optional overflow label when you choose to render attachment previews inside a queue row.
| Prop | Type | Default | Description |
|---|---|---|---|
hiddenCount | number | - | Number of attachments hidden beyond the visible previews |
noun | string | 'files' | Noun used in the overflow label |
Action buttons for queue rows. Remove and More include default labels and icons. Extend HeroUI Button props.