- Alert
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Checkbox Group
- ComboBox
- Command
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Disclosure
- Disclosure Group
- Drawer
- Empty
- Field
- File Trigger
- Form
- Grid List
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- ListBox
- Menu
- Number Field
- Pagination
- Popover
- Progress Bar
- Radio Group
- Range Calendar
- Resizable
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Tag Group
- Text Field
- Textarea
- Time Field
- Toast
- Toggle Button
- Toggle Button Group
- Tooltip
- Tree
- Typography
"use client"
import { useState } from "react"
import { GlobeIcon } from "lucide-react"
import {
PromptInput,
PromptInputActionAddAttachments,
PromptInputActionMenu,
PromptInputActionMenuContent,
PromptInputActionMenuTrigger,
PromptInputAttachment,
PromptInputAttachments,
PromptInputBody,
PromptInputButton,
PromptInputFooter,
PromptInputModelSelect,
PromptInputModelSelectItem,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputTextarea,
PromptInputTools,
} from "@/components/ai-elements/prompt-input"
const models = [
{ id: "gpt-4o", name: "GPT-4o" },
{ id: "claude-opus-4-20250514", name: "Claude 4 Opus" },
]
export function PromptInputDemo() {
const [text, setText] = useState("")
const [model, setModel] = useState("gpt-4o")
const [useWebSearch, setUseWebSearch] = useState(false)
const [status, setStatus] = useState<"ready" | "submitted" | "streaming">(
"ready"
)
const handleSubmit = (message: { text?: string; files?: any[] }) => {
if (!message.text?.trim() && !message.files?.length) {
return
}
console.log("Submitted:", {
text: message.text,
files: message.files,
model,
webSearch: useWebSearch,
})
setStatus("submitted")
setTimeout(() => {
setStatus("streaming")
setTimeout(() => {
setStatus("ready")
setText("")
}, 2000)
}, 1000)
}
return (
<div className="w-full max-w-2xl">
<PromptInput
onSubmit={handleSubmit}
className="relative"
globalDrop
multiple
>
<PromptInputBody>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputTextarea
onChange={(e) => setText(e.target.value)}
value={text}
placeholder="Ask me anything..."
/>
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
<PromptInputActionMenu>
<PromptInputActionMenuTrigger />
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments />
</PromptInputActionMenuContent>
</PromptInputActionMenu>
<PromptInputSpeechButton onTranscriptionChange={setText} />
<PromptInputButton
onClick={() => setUseWebSearch(!useWebSearch)}
variant={useWebSearch ? "default" : "ghost"}
>
<GlobeIcon size={16} />
<span>Search</span>
</PromptInputButton>
<PromptInputModelSelect
selectedKey={model}
onSelectionChange={(key) => setModel(key as string)}
items={models}
>
{(item) => (
<PromptInputModelSelectItem id={item.id}>
{item.name}
</PromptInputModelSelectItem>
)}
</PromptInputModelSelect>
</PromptInputTools>
<PromptInputSubmit
disabled={!text && status === "ready"}
status={status}
/>
</PromptInputFooter>
</PromptInput>
</div>
)
}
Installation
pnpmnpmyarnbunpnpm dlx shadcn@latest add prompt-input
Usage
import {
PromptInput,
PromptInputActionAddAttachments,
PromptInputActionMenu,
PromptInputActionMenuContent,
PromptInputActionMenuTrigger,
PromptInputAttachment,
PromptInputAttachments,
PromptInputBody,
PromptInputButton,
PromptInputFooter,
PromptInputModelSelect,
PromptInputModelSelectItem,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputTextarea,
PromptInputTools,
} from "@/components/ai-elements/prompt-input"const [text, setText] = useState("")
const [model, setModel] = useState("gpt-4o")
const models = [
{ id: "gpt-4o", name: "GPT-4o" },
{ id: "claude-opus-4-20250514", name: "Claude 4 Opus" },
]
<PromptInput onSubmit={(message) => console.log(message)}>
<PromptInputBody>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputTextarea
onChange={(e) => setText(e.target.value)}
value={text}
/>
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
<PromptInputActionMenu>
<PromptInputActionMenuTrigger />
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments />
</PromptInputActionMenuContent>
</PromptInputActionMenu>
<PromptInputModelSelect
selectedKey={model}
onSelectionChange={(key) => setModel(key as string)}
items={models}
>
{(item) => (
<PromptInputModelSelectItem id={item.id}>
{item.name}
</PromptInputModelSelectItem>
)}
</PromptInputModelSelect>
</PromptInputTools>
<PromptInputSubmit />
</PromptInputFooter>
</PromptInput>Examples
Default
"use client"
import { useState } from "react"
import { GlobeIcon } from "lucide-react"
import {
PromptInput,
PromptInputActionAddAttachments,
PromptInputActionMenu,
PromptInputActionMenuContent,
PromptInputActionMenuTrigger,
PromptInputAttachment,
PromptInputAttachments,
PromptInputBody,
PromptInputButton,
PromptInputFooter,
PromptInputModelSelect,
PromptInputModelSelectItem,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputTextarea,
PromptInputTools,
} from "@/components/ai-elements/prompt-input"
const models = [
{ id: "gpt-4o", name: "GPT-4o" },
{ id: "claude-opus-4-20250514", name: "Claude 4 Opus" },
]
export function PromptInputDemo() {
const [text, setText] = useState("")
const [model, setModel] = useState("gpt-4o")
const [useWebSearch, setUseWebSearch] = useState(false)
const [status, setStatus] = useState<"ready" | "submitted" | "streaming">(
"ready"
)
const handleSubmit = (message: { text?: string; files?: any[] }) => {
if (!message.text?.trim() && !message.files?.length) {
return
}
console.log("Submitted:", {
text: message.text,
files: message.files,
model,
webSearch: useWebSearch,
})
setStatus("submitted")
setTimeout(() => {
setStatus("streaming")
setTimeout(() => {
setStatus("ready")
setText("")
}, 2000)
}, 1000)
}
return (
<div className="w-full max-w-2xl">
<PromptInput
onSubmit={handleSubmit}
className="relative"
globalDrop
multiple
>
<PromptInputBody>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputTextarea
onChange={(e) => setText(e.target.value)}
value={text}
placeholder="Ask me anything..."
/>
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
<PromptInputActionMenu>
<PromptInputActionMenuTrigger />
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments />
</PromptInputActionMenuContent>
</PromptInputActionMenu>
<PromptInputSpeechButton onTranscriptionChange={setText} />
<PromptInputButton
onClick={() => setUseWebSearch(!useWebSearch)}
variant={useWebSearch ? "default" : "ghost"}
>
<GlobeIcon size={16} />
<span>Search</span>
</PromptInputButton>
<PromptInputModelSelect
selectedKey={model}
onSelectionChange={(key) => setModel(key as string)}
items={models}
>
{(item) => (
<PromptInputModelSelectItem id={item.id}>
{item.name}
</PromptInputModelSelectItem>
)}
</PromptInputModelSelect>
</PromptInputTools>
<PromptInputSubmit
disabled={!text && status === "ready"}
status={status}
/>
</PromptInputFooter>
</PromptInput>
</div>
)
}
Features
- File Attachments: Support for multiple file uploads with drag & drop
- Image Preview: Preview images before sending with automatic detection
- Auto-resize: Textarea automatically grows with content
- Keyboard Shortcuts: Submit with Enter (Shift+Enter for new line)
- Paste Support: Paste images directly from clipboard
- Speech Input: Built-in voice transcription using Web Speech API
- Model Selection: Dropdown to select AI models
- Custom Actions: Add custom buttons and tools
- Stop Button: Automatically shows stop button during streaming
- Global Drop: Optional document-wide drag & drop support
Anatomy
The PromptInput component is composed of several sub-components that work together:
<PromptInput>
<PromptInputBody>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputTextarea />
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
{/* Action menu, buttons, model select */}
</PromptInputTools>
<PromptInputSubmit />
</PromptInputFooter>
</PromptInput>API Reference
PromptInput
Main container that handles form submission and file management.
| Prop | Type | Default | Description |
|---|---|---|---|
onSubmit | (message: PromptInputMessage, event: FormEvent) => void | Promise<void> | - | Called when form is submitted |
accept | string | - | File types to accept (e.g., "image/*") |
multiple | boolean | false | Allow multiple file uploads |
globalDrop | boolean | false | Enable document-wide drag & drop |
syncHiddenInput | boolean | false | Keep hidden input synced for native forms |
maxFiles | number | - | Maximum number of files allowed |
maxFileSize | number | - | Maximum file size in bytes |
onError | (err: { code: string, message: string }) => void | - | Error handler for file validation |
PromptInputBody
Container for the textarea and attachments display.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Custom CSS classes |
PromptInputTextarea
Auto-resizing textarea that handles keyboard shortcuts.
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | "What would you like to know?" | Placeholder text |
value | string | - | Input value (controlled) |
onChange | (e: ChangeEvent<HTMLTextAreaElement>) => void | - | Change handler |
className | string | - | Custom CSS classes |
PromptInputAttachments
Displays file attachments using a render prop pattern.
| Prop | Type | Default | Description |
|---|---|---|---|
children | (attachment: FileUIPart & { id: string }) => ReactNode | - | Render function for each attachment |
className | string | - | Custom CSS classes |
PromptInputAttachment
Renders a single attachment with preview and remove button.
| Prop | Type | Default | Description |
|---|---|---|---|
data | FileUIPart & { id: string } | - | Attachment data to display |
className | string | - | Custom CSS classes |
PromptInputFooter
Container for tools and submit button.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Custom CSS classes |
PromptInputTools
Container for action buttons and controls.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Custom CSS classes |
PromptInputButton
Base button component for tools.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | string | "ghost" | Button variant |
size | string | Auto-calculated | Button size |
className | string | - | Custom CSS classes |
PromptInputActionMenu
Dropdown menu for additional actions.
| Prop | Type | Default | Description |
|---|---|---|---|
Inherits props from DropdownMenu |
PromptInputActionAddAttachments
Pre-built menu item to trigger file dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Add photos or files" | Menu item label |
PromptInputSubmit
Submit button that changes based on status.
| Prop | Type | Default | Description |
|---|---|---|---|
status | ChatStatus | - | Current chat status (shows loading/stop icon) |
disabled | boolean | - | Disable button |
variant | string | "default" | Button variant |
size | string | "icon-sm" | Button size |
PromptInputSpeechButton
Button with built-in speech recognition.
| Prop | Type | Default | Description |
|---|---|---|---|
textareaRef | RefObject<HTMLTextAreaElement> | - | Ref to textarea for inserting text |
onTranscriptionChange | (text: string) => void | - | Called when transcription changes |
PromptInputModelSelect
Dropdown for selecting AI models. Built on react-aria-components Select.
| Prop | Type | Default | Description |
|---|---|---|---|
selectedKey | string | - | Selected model key |
onSelectionChange | (key: Key) => void | - | Called when selection changes |
items | Iterable<T> | - | List of model objects |
children | (item: T) => ReactNode | - | Render function for items |
placeholder | string | - | Placeholder text when no selection |
PromptInputModelSelectItem
Individual item in the model select dropdown.
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique identifier for the item |
children | ReactNode | - | Item content |
PromptInputProvider
Optional provider for global state management. When used, allows controlling input state from outside the component.
| Prop | Type | Default | Description |
|---|---|---|---|
initialInput | string | "" | Initial text value |
children | ReactNode | - | Child components |
Hooks
usePromptInputAttachments
Access attachments context from within a PromptInput or PromptInputProvider.
const attachments = usePromptInputAttachments()
// attachments.files, attachments.add(), attachments.remove(), attachments.clear()usePromptInputController
Access the full controller from a PromptInputProvider.
const controller = usePromptInputController()
// controller.textInput, controller.attachmentsOn This Page
InstallationUsageExamplesDefaultFeaturesAnatomyAPI ReferencePromptInputPromptInputBodyPromptInputTextareaPromptInputAttachmentsPromptInputAttachmentPromptInputFooterPromptInputToolsPromptInputButtonPromptInputActionMenuPromptInputActionAddAttachmentsPromptInputSubmitPromptInputSpeechButtonPromptInputModelSelectPromptInputModelSelectItemPromptInputProviderHooksusePromptInputAttachmentsusePromptInputController