Prompt Input Components
Advanced text input components with file attachments, drag-and-drop, speech input, and comprehensive state management.
Table of Contents
- Core Components
- State Management
- Attachment Handling
- Action Menus
- Submit Button
- Speech Input
- Advanced Features
- Complete Example
Core Components
PromptInput
Main form container that handles text input, file attachments, and submission.
type PromptInputProps = Omit<HTMLAttributes<HTMLFormElement>, "onSubmit" | "onError"> & {
accept?: string;
multiple?: boolean;
globalDrop?: boolean;
syncHiddenInput?: boolean;
maxFiles?: number;
maxFileSize?: number;
onError?: (err: { code: "max_files" | "max_file_size" | "accept"; message: string }) => void;
onSubmit: (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => void | Promise<void>;
};
type PromptInputMessage = {
text: string;
files: FileUIPart[];
};
Props:
accept?: string- File type filter (e.g., "image/*")multiple?: boolean- Allow multiple file selectionglobalDrop?: boolean- Accept drops anywhere on document (default: false)syncHiddenInput?: boolean- Keep hidden input in sync (default: false)maxFiles?: number- Maximum number of filesmaxFileSize?: number- Maximum file size in bytesonError?: (err) => void- Error handler for file validationonSubmit: (message, event) => void | Promise<void>- Submit handler (required)
Usage:
<PromptInput
accept="image/*"
multiple
maxFiles={5}
maxFileSize={10 * 1024 * 1024} // 10MB
=> toast.error(err.message)}
(message) => {
await sendMessage(message.text, message.files);
}}
>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputBody>
<PromptInputTextarea placeholder="Type a message..." />
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
<PromptInputButton => attachments.openFileDialog()}>
<PaperclipIcon />
</PromptInputButton>
</PromptInputTools>
<PromptInputSubmit status={chatStatus} />
</PromptInputFooter>
</PromptInput>
Features:
- Dual-mode operation (controlled/uncontrolled)
- Drag-and-drop file handling (local or global)
- Paste image/file support
- File validation (type, size, count)
- Automatic blob URL to data URL conversion
- Async/sync onSubmit support
- Auto-reset on successful submission
PromptInputBody
Container for the main input area.
type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;
Usage:
<PromptInputBody>
<PromptInputTextarea />
</PromptInputBody>
PromptInputTextarea
Auto-expanding textarea with keyboard shortcuts and paste handling.
type PromptInputTextareaProps = ComponentProps<typeof InputGroupTextarea>;
Props:
placeholder?: string- Placeholder text (default: "What would you like to know?")- Standard textarea props
Usage:
<PromptInputTextarea
placeholder="Ask me anything..."
className="max-h-48 min-h-16"
/>
Keyboard Shortcuts:
Enter- Submit (without Shift)Shift+Enter- New lineBackspace- Remove last attachment when textarea is empty
Features:
- Auto-expands with content (field-sizing-content)
- Paste image/file support
- Composition event handling (for IME)
- Respects submit button disabled state
- Controlled/uncontrolled dual-mode
PromptInputHeader
Header section for additional controls above the textarea.
type PromptInputHeaderProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;
Usage:
<PromptInputHeader>
<PromptInputSelect>
<PromptInputSelectTrigger>
<PromptInputSelectValue placeholder="Select model" />
</PromptInputSelectTrigger>
<PromptInputSelectContent>
<PromptInputSelectItem value="gpt-4">GPT-4</PromptInputSelectItem>
<PromptInputSelectItem value="claude">Claude</PromptInputSelectItem>
</PromptInputSelectContent>
</PromptInputSelect>
</PromptInputHeader>
PromptInputFooter
Footer section for tools and submit button.
type PromptInputFooterProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;
Usage:
<PromptInputFooter>
<PromptInputTools>
{/* Tool buttons */}
</PromptInputTools>
<PromptInputSubmit status="ready" />
</PromptInputFooter>
PromptInputTools
Container for tool buttons in the footer.
type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;
Usage:
<PromptInputTools>
<PromptInputButton
<PaperclipIcon />
</PromptInputButton>
<PromptInputSpeechButton textareaRef={textareaRef} />
</PromptInputTools>
PromptInputButton
Generic button for actions and tools.
type PromptInputButtonProps = ComponentProps<typeof InputGroupButton>;
Props:
variant?: ButtonVariant- Button style (default: "ghost")size?: ButtonSize- Button size (auto-determined based on children)
Usage:
<PromptInputButton
<IconComponent />
</PromptInputButton>
<PromptInputButton
<IconComponent />
Label
</PromptInputButton>
State Management
PromptInputProvider
Optional global provider that lifts input and attachment state outside of PromptInput.
type PromptInputProviderProps = PropsWithChildren<{
initialInput?: string;
}>;
Usage:
<PromptInputProvider initialInput="">
{/* App content */}
<PromptInput
{/* Input content */}
</PromptInput>
{/* External components can access state */}
<ExternalComponent />
</PromptInputProvider>
Provides:
textInput: TextInputContext- Text state and settersattachments: AttachmentsContext- File state and methods__registerFileInput- Internal registration method
usePromptInputController
Hook to access the provider state.
const usePromptInputController = () => {
const { textInput, attachments } = usePromptInputController();
return {
textInput: {
value: string;
setInput: (v: string) => void;
clear: () => void;
},
attachments: {
files: (FileUIPart & { id: string })[];
add: (files: File[] | FileList) => void;
remove: (id: string) => void;
clear: () => void;
openFileDialog: () => void;
fileInputRef: RefObject<HTMLInputElement | null>;
}
};
};
Usage:
function ExternalComponent() {
const { textInput, attachments } = usePromptInputController();
return (
<div>
<p>Current input: {textInput.value}</p>
<p>Attachments: {attachments.files.length}</p>
<Button => textInput.clear()}>Clear</Button>
</div>
);
}
useProviderAttachments
Hook to access attachment state from provider.
const useProviderAttachments = () => AttachmentsContext;
usePromptInputAttachments
Hook to access attachment state (dual-mode: provider or local).
const usePromptInputAttachments = () => AttachmentsContext;
Attachment Handling
PromptInputAttachments
Container for rendering attachments.
type PromptInputAttachmentsProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
children: (attachment: FileUIPart & { id: string }) => ReactNode;
};
Usage:
<PromptInputAttachments>
{(attachment) => (
<PromptInputAttachment data={attachment} />
)}
</PromptInputAttachments>
Behavior:
- Only renders if attachments exist
- Uses render prop pattern for flexibility
PromptInputAttachment
Individual attachment display with preview and remove button.
type PromptInputAttachmentProps = HTMLAttributes<HTMLDivElement> & {
data: FileUIPart & { id: string };
className?: string;
};
Usage:
<PromptInputAttachment
data={{
id: "abc123",
type: "file",
url: "blob:...",
filename: "image.png",
mediaType: "image/png"
}}
/>
Features:
- Image preview for image/* media types
- Paperclip icon for other files
- Hover to reveal remove button
- Hover card with full preview
- Truncated filename display
PromptInputHoverCard
Hover card for attachment preview.
type PromptInputHoverCardProps = ComponentProps<typeof HoverCard>;
Default Delays:
openDelay: 0- Instant opencloseDelay: 0- Instant close
PromptInputHoverCardContent
Content area for hover card preview.
type PromptInputHoverCardContentProps = ComponentProps<typeof HoverCardContent>;
Action Menus
PromptInputActionMenu
Dropdown menu for additional actions.
type PromptInputActionMenuProps = ComponentProps<typeof DropdownMenu>;
Usage:
<PromptInputActionMenu>
<PromptInputActionMenuTrigger>
<PlusIcon />
</PromptInputActionMenuTrigger>
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments label="Add files" />
<PromptInputActionMenuItem>
<SettingsIcon className="mr-2 size-4" />
Settings
</PromptInputActionMenuItem>
</PromptInputActionMenuContent>
</PromptInputActionMenu>
PromptInputActionMenuTrigger
Trigger button for action menu.
type PromptInputActionMenuTriggerProps = PromptInputButtonProps;
Default Icon: PlusIcon
PromptInputActionMenuContent
Content area for menu items.
type PromptInputActionMenuContentProps = ComponentProps<typeof DropdownMenuContent>;
Default Alignment: align="start"
PromptInputActionMenuItem
Individual menu item.
type PromptInputActionMenuItemProps = ComponentProps<typeof DropdownMenuItem>;
PromptInputActionAddAttachments
Pre-built menu item for adding attachments.
type PromptInputActionAddAttachmentsProps = ComponentProps<typeof DropdownMenuItem> & {
label?: string;
};
Props:
label?: string- Button label (default: "Add photos or files")
Usage:
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments label="Upload images" />
</PromptInputActionMenuContent>
Submit Button
PromptInputSubmit
Status-aware submit button with dynamic icons.
type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
status?: ChatStatus; // "submitted" | "streaming" | "error" | "ready"
};
Status Icons:
undefined/"ready"-CornerDownLeftIcon(enter key)"submitted"-Loader2Icon(spinning)"streaming"-SquareIcon(stop)"error"-XIcon(error)
Usage:
<PromptInputSubmit status={chatStatus} />
Behavior:
type="submit"- Triggers form submissionaria-label="Submit"- Accessible label
Speech Input
PromptInputSpeechButton
Voice input button using Web Speech API.
type PromptInputSpeechButtonProps = ComponentProps<typeof PromptInputButton> & {
textareaRef?: RefObject<HTMLTextAreaElement | null>;
onTranscriptionChange?: (text: string) => void;
};
Props:
textareaRef?: RefObject- Reference to textarea for text insertiononTranscriptionChange?: (text: string) => void- Callback when text changes
Usage:
const textareaRef = useRef<HTMLTextAreaElement>(null);
<PromptInputTextarea ref={textareaRef} />
<PromptInputSpeechButton
textareaRef={textareaRef}
=> console.log("Transcribed:", text)}
/>
Features:
- Browser-based speech recognition
- Continuous recording mode
- Interim results support
- Automatic text insertion
- Visual feedback (pulse animation when listening)
- Disabled if browser doesn't support Speech Recognition
- Error handling
Advanced Features
Select Components
For model selection or other dropdowns.
<PromptInputSelect value={model}
<PromptInputSelectTrigger>
<PromptInputSelectValue placeholder="Select model" />
</PromptInputSelectTrigger>
<PromptInputSelectContent>
<PromptInputSelectItem value="gpt-4">GPT-4</PromptInputSelectItem>
<PromptInputSelectItem value="claude">Claude</PromptInputSelectItem>
</PromptInputSelectContent>
</PromptInputSelect>
Command Components
For slash commands or autocomplete.
<PromptInputCommand>
<PromptInputCommandInput placeholder="Search commands..." />
<PromptInputCommandList>
<PromptInputCommandEmpty>No commands found</PromptInputCommandEmpty>
<PromptInputCommandGroup heading="Actions">
<PromptInputCommandItem value="summarize">
Summarize
</PromptInputCommandItem>
<PromptInputCommandItem value="translate">
Translate
</PromptInputCommandItem>
</PromptInputCommandGroup>
<PromptInputCommandSeparator />
<PromptInputCommandGroup heading="Settings">
<PromptInputCommandItem value="preferences">
Preferences
</PromptInputCommandItem>
</PromptInputCommandGroup>
</PromptInputCommandList>
</PromptInputCommand>
Tab Components
For organizing input modes or templates.
<PromptInputTabsList>
<PromptInputTab>
<PromptInputTabLabel>Templates</PromptInputTabLabel>
<PromptInputTabBody>
<PromptInputTabItem>Summarize article</PromptInputTabItem>
<PromptInputTabItem>Write email</PromptInputTabItem>
</PromptInputTabBody>
</PromptInputTab>
</PromptInputTabsList>
Complete Example
import { useRef, useState } from "react";
import {
PromptInput,
PromptInputProvider,
PromptInputAttachments,
PromptInputAttachment,
PromptInputBody,
PromptInputTextarea,
PromptInputFooter,
PromptInputTools,
PromptInputButton,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputActionMenu,
PromptInputActionMenuTrigger,
PromptInputActionMenuContent,
PromptInputActionAddAttachments,
usePromptInputAttachments,
} from "@/components/ai-elements/prompt-input";
import { PaperclipIcon, PlusIcon } from "lucide-react";
function ChatInput() {
const [status, setStatus] = useState<ChatStatus>("ready");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const attachments = usePromptInputAttachments();
const handleSubmit = async (message: PromptInputMessage) => {
setStatus("submitted");
try {
await sendMessage(message.text, message.files);
setStatus("ready");
} catch (error) {
setStatus("error");
}
};
return (
<PromptInput
accept="image/*,.pdf,.doc,.docx"
multiple
maxFiles={10}
maxFileSize={10 * 1024 * 1024}
=> toast.error(err.message)}
>
<PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments>
<PromptInputBody>
<PromptInputTextarea
ref={textareaRef}
placeholder="Type a message..."
/>
</PromptInputBody>
<PromptInputFooter>
<PromptInputTools>
<PromptInputButton => attachments.openFileDialog()}>
<PaperclipIcon className="size-4" />
</PromptInputButton>
<PromptInputSpeechButton textareaRef={textareaRef} />
<PromptInputActionMenu>
<PromptInputActionMenuTrigger>
<PlusIcon className="size-4" />
</PromptInputActionMenuTrigger>
<PromptInputActionMenuContent>
<PromptInputActionAddAttachments />
<PromptInputActionMenuItem>
Insert template
</PromptInputActionMenuItem>
</PromptInputActionMenuContent>
</PromptInputActionMenu>
</PromptInputTools>
<PromptInputSubmit status={status} />
</PromptInputFooter>
</PromptInput>
);
}
// With global provider
function App() {
return (
<PromptInputProvider initialInput="">
<ChatInput />
</PromptInputProvider>
);
}