# Prompt Input
URL: /components/prompt-input
---
title: Prompt Input
description: Allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model.
path: elements/components/prompt-input
---
The `PromptInput` component allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model.
## Installation
## Usage
```tsx
import {
PromptInput,
PromptInputActionAddAttachments,
PromptInputActionMenu,
PromptInputActionMenuContent,
PromptInputActionMenuItem,
PromptInputActionMenuTrigger,
PromptInputAttachment,
PromptInputAttachments,
PromptInputBody,
PromptInputButton,
PromptInputProvider,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputTextarea,
PromptInputFooter,
PromptInputTools,
usePromptInputAttachments,
} from "@/components/ai-elements/prompt-input";
```
```tsx
import { GlobeIcon } from "lucide-react";
{}} className="mt-4 relative">
{(attachment) => }
{}} value={""} />
Search
{}} value="gpt-4o">
GPT-4o
Claude 4 Opus
;
```
## Usage with AI SDK
Build a fully functional chat app using `PromptInput`, [`Conversation`](/elements/components/conversation) with a model picker:
Add the following component to your frontend:
```tsx title="app/page.tsx"
"use client";
import {
PromptInput,
PromptInputActionAddAttachments,
PromptInputActionMenu,
PromptInputActionMenuContent,
PromptInputActionMenuTrigger,
PromptInputAttachment,
PromptInputAttachments,
PromptInputBody,
PromptInputButton,
type PromptInputMessage,
PromptInputModelSelect,
PromptInputModelSelectContent,
PromptInputModelSelectItem,
PromptInputModelSelectTrigger,
PromptInputModelSelectValue,
PromptInputSpeechButton,
PromptInputSubmit,
PromptInputTextarea,
PromptInputFooter,
PromptInputTools,
} from "@/components/ai-elements/prompt-input";
import { GlobeIcon } from "lucide-react";
import { useRef, useState } from "react";
import { useChat } from "@ai-sdk/react";
import { Conversation, ConversationContent, ConversationScrollButton } from "@/components/ai-elements/conversation";
import { Message, MessageContent } from "@/components/ai-elements/message";
import { Response } from "@/components/ai-elements/response";
const models = [
{ id: "gpt-4o", name: "GPT-4o" },
{ id: "claude-opus-4-20250514", name: "Claude 4 Opus" },
];
const InputDemo = () => {
const [text, setText] = useState("");
const [model, setModel] = useState(models[0].id);
const [useWebSearch, setUseWebSearch] = useState(false);
const textareaRef = useRef(null);
const { messages, status, sendMessage } = useChat();
const handleSubmit = (message: PromptInputMessage) => {
const hasText = Boolean(message.text);
const hasAttachments = Boolean(message.files?.length);
if (!(hasText || hasAttachments)) {
return;
}
sendMessage(
{
text: message.text || "Sent with attachments",
files: message.files,
},
{
body: {
model: model,
webSearch: useWebSearch,
},
}
);
setText("");
};
return (
{messages.map((message) => (
{message.parts.map((part, i) => {
switch (part.type) {
case "text":
return {part.text};
default:
return null;
}
})}
))}
{(attachment) => }
setText(e.target.value)} ref={textareaRef} value={text} />
setUseWebSearch(!useWebSearch)} variant={useWebSearch ? "default" : "ghost"}>
Search
{
setModel(value);
}}
value={model}
>
{models.map((model) => (
{model.name}
))}
);
};
export default InputDemo;
```
Add the following route to your backend:
```ts title="app/api/chat/route.ts"
import { streamText, UIMessage, convertToModelMessages } from "ai";
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const {
model,
messages,
webSearch,
}: {
messages: UIMessage[];
model: string;
webSearch?: boolean;
} = await req.json();
const result = streamText({
model: webSearch ? "perplexity/sonar" : model,
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
```
## Features
- Auto-resizing textarea that adjusts height based on content
- File attachment support with drag-and-drop
- Image preview for image attachments
- Configurable file constraints (max files, max size, accepted types)
- Automatic submit button icons based on status
- Support for keyboard shortcuts (Enter to submit, Shift+Enter for new line)
- Customizable min/max height for the textarea
- Flexible toolbar with support for custom actions and tools
- Built-in model selection dropdown
- Built-in native speech recognition button (Web Speech API)
- Optional provider for lifted state management
- Form automatically resets on submit
- Responsive design with mobile-friendly controls
- Clean, modern styling with customizable themes
- Form-based submission handling
- Hidden file input sync for native form posts
- Global document drop support (opt-in)
## Examples
### Cursor style
## Props
### ``
void',
},
accept: {
description: 'File types to accept (e.g., "image/*"). Leave undefined for any.',
type: 'string',
},
multiple: {
description: 'Whether to allow multiple file selection.',
type: 'boolean',
},
globalDrop: {
description: 'When true, accepts file drops anywhere on the document.',
type: 'boolean',
},
syncHiddenInput: {
description: 'Render a hidden input with given name for native form posts.',
type: 'boolean',
},
maxFiles: {
description: 'Maximum number of files allowed.',
type: 'number',
},
maxFileSize: {
description: 'Maximum file size in bytes.',
type: 'number',
},
onError: {
description: 'Handler for file validation errors.',
type: '(err: { code: "max_files" | "max_file_size" | "accept", message: string }) => void',
},
'...props': {
description: 'Any other props are spread to the root form element.',
type: 'React.HTMLAttributes',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
React.ReactNode',
},
'...props': {
description: 'Any other props are spread to the attachments container.',
type: 'React.HTMLAttributes',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
Optional global provider that lifts PromptInput state outside of PromptInput. When used, it allows you to access and control the input state from anywhere within the provider tree. If not used, PromptInput stays fully self-managed.
### ``
',
},
onTranscriptionChange: {
description: 'Callback fired when transcription text changes.',
type: '(text: string) => void',
},
'...props': {
description: 'Any other props are spread to the underlying PromptInputButton component.',
type: 'React.ComponentProps',
},
}}
/>
Built-in button component that provides native speech recognition using the Web Speech API. The button will be disabled if speech recognition is not supported in the browser. Displays a microphone icon and pulses while actively listening.
## Hooks
### `usePromptInputAttachments`
Access and manage file attachments within a PromptInput context.
```tsx
const attachments = usePromptInputAttachments();
// Available methods:
attachments.files; // Array of current attachments
attachments.add(files); // Add new files
attachments.remove(id); // Remove an attachment by ID
attachments.clear(); // Clear all attachments
attachments.openFileDialog(); // Open file selection dialog
```
### `usePromptInputController`
Access the full PromptInput controller from a PromptInputProvider. Only available when using the provider.
```tsx
const controller = usePromptInputController();
// Available methods:
controller.textInput.value; // Current text input value
controller.textInput.setInput(value); // Set text input value
controller.textInput.clear(); // Clear text input
controller.attachments; // Same as usePromptInputAttachments
```
### `useProviderAttachments`
Access attachments context from a PromptInputProvider. Only available when using the provider.
```tsx
const attachments = useProviderAttachments();
// Same interface as usePromptInputAttachments
```
### ``
, "align">',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>
### ``
',
},
}}
/>