• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
drewmcdonald

drewmcdonald

promptCompare

Public
Like
promptCompare
Home
Code
7
.claude
3
backend
2
docs
1
frontend
4
shared
.mcp.json
deno.json
Environment variables
2
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
.claude
/
skills
/
ai-elements
/
references
/
prompt-input.md
Code
/
.claude
/
skills
/
ai-elements
/
references
/
prompt-input.md
Search
…
Viewing readonly version of main branch: v115
View latest version
prompt-input.md

Prompt Input

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.

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.

See scripts/prompt-input.tsx for this example.

Installation

npx ai-elements@latest add prompt-input

Usage with AI SDK

Build a fully functional chat app using PromptInput, Conversation with a model picker:

Add the following component to your frontend:

Create val
"use client"; import { Attachment, AttachmentPreview, AttachmentRemove, Attachments, } from "@/components/ai-elements/attachments"; import { PromptInput, PromptInputActionAddAttachments, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuTrigger, PromptInputBody, PromptInputButton, PromptInputFooter, PromptInputHeader, type PromptInputMessage, PromptInputSelect, PromptInputSelectContent, PromptInputSelectItem, PromptInputSelectTrigger, PromptInputSelectValue, PromptInputSubmit, PromptInputTextarea, PromptInputTools, usePromptInputAttachments, } from "@/components/ai-elements/prompt-input"; import { GlobeIcon } from "lucide-react"; import { useState } from "react"; import { useChat } from "@ai-sdk/react"; import { Conversation, ConversationContent, ConversationScrollButton, } from "@/components/ai-elements/conversation"; import { Message, MessageContent, MessageResponse, } from "@/components/ai-elements/message"; const PromptInputAttachmentsDisplay = () => { const attachments = usePromptInputAttachments(); if (attachments.files.length === 0) { return null; } return ( <Attachments variant="inline"> {attachments.files.map((attachment) => ( <Attachment data={attachment} key={attachment.id} onRemove={() => attachments.remove(attachment.id)} > <AttachmentPreview /> <AttachmentRemove /> </Attachment> ))} </Attachments> ); }; const models = [ { id: "gpt-4o", name: "GPT-4o" }, { id: "claude-opus-4-20250514", name: "Claude 4 Opus" }, ]; const InputDemo = () => { const [text, setText] = useState<string>(""); const [model, setModel] = useState<string>(models[0].id); const [useWebSearch, setUseWebSearch] = useState<boolean>(false); 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 ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <div className="flex flex-col h-full"> <Conversation> <ConversationContent> {messages.map((message) => ( <Message from={message.role} key={message.id}> <MessageContent> {message.parts.map((part, i) => { switch (part.type) { case "text": return ( <MessageResponse key={`${message.id}-${i}`}> {part.text} </MessageResponse> ); default: return null; } })} </MessageContent> </Message> ))} </ConversationContent> <ConversationScrollButton /> </Conversation> <PromptInput onSubmit={handleSubmit} className="mt-4" globalDrop multiple > <PromptInputHeader> <PromptInputAttachmentsDisplay /> </PromptInputHeader> <PromptInputBody> <PromptInputTextarea onChange={(e) => setText(e.target.value)} value={text} /> </PromptInputBody> <PromptInputFooter> <PromptInputTools> <PromptInputActionMenu> <PromptInputActionMenuTrigger /> <PromptInputActionMenuContent> <PromptInputActionAddAttachments /> </PromptInputActionMenuContent> </PromptInputActionMenu> <PromptInputButton onClick={() => setUseWebSearch(!useWebSearch)} tooltip={{ content: "Search the web", shortcut: "⌘K" }} variant={useWebSearch ? "default" : "ghost"} > <GlobeIcon size={16} /> <span>Search</span> </PromptInputButton> <PromptInputSelect onValueChange={(value) => { setModel(value); }} value={model} > <PromptInputSelectTrigger> <PromptInputSelectValue /> </PromptInputSelectTrigger> <PromptInputSelectContent> {models.map((model) => ( <PromptInputSelectItem key={model.id} value={model.id}> {model.name} </PromptInputSelectItem> ))} </PromptInputSelectContent> </PromptInputSelect> </PromptInputTools> <PromptInputSubmit disabled={!text && !status} status={status} /> </PromptInputFooter> </PromptInput> </div> </div> ); }; export default InputDemo;

Add the following route to your backend:

Create val
import { convertToModelMessages, streamText, UIMessage } 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: await 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

See scripts/prompt-input-cursor.tsx for this example.

Button tooltips

Buttons can display tooltips with optional keyboard shortcut hints. Hover over the buttons below to see the tooltips.

See scripts/prompt-input-tooltip.tsx for this example.

Props

<PromptInput />

PropTypeDefaultDescription
onSubmit(message: PromptInputMessage, event: FormEvent) => void-Handler called when the form is submitted with message text and files.
acceptstring-File types to accept (e.g.,
multipleboolean-Whether to allow multiple file selection.
globalDropboolean-When true, accepts file drops anywhere on the document.
syncHiddenInputboolean-Render a hidden input with given name for native form posts.
maxFilesnumber-Maximum number of files allowed.
maxFileSizenumber-Maximum file size in bytes.
onError(err: { code:-Handler for file validation errors.
...propsReact.HTMLAttributes<HTMLFormElement>-Any other props are spread to the root form element.

<PromptInputTextarea />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof Textarea>-Any other props are spread to the underlying Textarea component.

<PromptInputFooter />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the toolbar div.

<PromptInputTools />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the tools div.

<PromptInputButton />

PropTypeDefaultDescription
tooltip`string{ content: ReactNode; shortcut?: string; side?: `-
...propsReact.ComponentProps<typeof Button>-Any other props are spread to the underlying shadcn/ui Button component.

Tooltip Examples

Create val
// Simple string tooltip <PromptInputButton tooltip="Search the web"> <GlobeIcon size={16} /> </PromptInputButton> // Tooltip with keyboard shortcut hint <PromptInputButton tooltip={{ content: "Search", shortcut: "⌘K" }}> <GlobeIcon size={16} /> </PromptInputButton> // Tooltip with custom position <PromptInputButton tooltip={{ content: "Search", side: "bottom" }}> <GlobeIcon size={16} /> </PromptInputButton>

<PromptInputSubmit />

PropTypeDefaultDescription
statusChatStatus-Current chat status to determine button icon (submitted, streaming, error).
...propsReact.ComponentProps<typeof Button>-Any other props are spread to the underlying shadcn/ui Button component.

<PromptInputSelect />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof Select>-Any other props are spread to the underlying Select component.

<PromptInputSelectTrigger />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof SelectTrigger>-Any other props are spread to the underlying SelectTrigger component.

<PromptInputSelectContent />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof SelectContent>-Any other props are spread to the underlying SelectContent component.

<PromptInputSelectItem />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof SelectItem>-Any other props are spread to the underlying SelectItem component.

<PromptInputSelectValue />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof SelectValue>-Any other props are spread to the underlying SelectValue component.

<PromptInputBody />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the body div.

Attachments

Attachment components have been moved to a separate module. See the Attachment component documentation for details on <Attachments />, <Attachment />, <AttachmentPreview />, <AttachmentInfo />, and <AttachmentRemove />.

<PromptInputActionMenu />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof DropdownMenu>-Any other props are spread to the underlying DropdownMenu component.

<PromptInputActionMenuTrigger />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof Button>-Any other props are spread to the underlying Button component.

<PromptInputActionMenuContent />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof DropdownMenuContent>-Any other props are spread to the underlying DropdownMenuContent component.

<PromptInputActionMenuItem />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof DropdownMenuItem>-Any other props are spread to the underlying DropdownMenuItem component.

<PromptInputActionAddAttachments />

PropTypeDefaultDescription
labelstring-Label for the menu item.
...propsReact.ComponentProps<typeof DropdownMenuItem>-Any other props are spread to the underlying DropdownMenuItem component.

<PromptInputProvider />

PropTypeDefaultDescription
initialInputstring-Initial text input value.
childrenReact.ReactNode-Child components that will have access to the provider context.

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.

<PromptInputHeader />

PropTypeDefaultDescription
...propsOmit<React.ComponentProps<typeof InputGroupAddon>,-Any other props (except align) are spread to the InputGroupAddon component.

<PromptInputHoverCard />

PropTypeDefaultDescription
openDelaynumber0Delay in milliseconds before opening.
closeDelaynumber0Delay in milliseconds before closing.
...propsReact.ComponentProps<typeof HoverCard>-Any other props are spread to the HoverCard component.

<PromptInputHoverCardTrigger />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof HoverCardTrigger>-Any other props are spread to the HoverCardTrigger component.

<PromptInputHoverCardContent />

PropTypeDefaultDescription
alignunknown-Alignment of the hover card content.
...propsReact.ComponentProps<typeof HoverCardContent>-Any other props are spread to the HoverCardContent component.

<PromptInputTabsList />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the div element.

<PromptInputTab />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the div element.

<PromptInputTabLabel />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLHeadingElement>-Any other props are spread to the h3 element.

<PromptInputTabBody />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the div element.

<PromptInputTabItem />

PropTypeDefaultDescription
...propsReact.HTMLAttributes<HTMLDivElement>-Any other props are spread to the div element.

<PromptInputCommand />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof Command>-Any other props are spread to the Command component.

<PromptInputCommandInput />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandInput>-Any other props are spread to the CommandInput component.

<PromptInputCommandList />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandList>-Any other props are spread to the CommandList component.

<PromptInputCommandEmpty />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandEmpty>-Any other props are spread to the CommandEmpty component.

<PromptInputCommandGroup />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandGroup>-Any other props are spread to the CommandGroup component.

<PromptInputCommandItem />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandItem>-Any other props are spread to the CommandItem component.

<PromptInputCommandSeparator />

PropTypeDefaultDescription
...propsReact.ComponentProps<typeof CommandSeparator>-Any other props are spread to the CommandSeparator component.

Hooks

usePromptInputAttachments

Access and manage file attachments within a PromptInput context.

Create val
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.

Create val
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.

Create val
const attachments = useProviderAttachments(); // Same interface as usePromptInputAttachments

usePromptInputReferencedSources

Access referenced sources context within a PromptInput.

Create val
const sources = usePromptInputReferencedSources(); // Available methods: sources.sources; // Array of current referenced sources sources.add(sources); // Add new source(s) sources.remove(id); // Remove a source by ID sources.clear(); // Clear all sources
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.