Prompt Input
Chat-style prompt field with optional multiline layout, attachments (picker + chips), a send / stop control for streaming, and a composable action strip.
Preview
"use client";
import { useState } from "react";
import {
AudioWave01Icon,
Megaphone02Icon,
RoboticIcon,
Upload01Icon,
ZapIcon,
} from "@hugeicons/core-free-icons";
import {
PromptInput,
PromptInputActions,
PromptInputPopup,
PromptInputPopupItem,
PromptInputPressAction,
PromptInputToggleAction,
} from "@/registry/promt-input";
export function PromptInputDemo() {
const [agent, setAgent] = useState(false);
const [streaming, setStreaming] = useState(false);
return (
<div className="flex w-full max-w-199 flex-col gap-4">
<PromptInput
isLoading={streaming}
onStop={() => setStreaming(false)}
onSubmit={() => setStreaming(true)}
placeholder="Ask anything…"
>
<PromptInputActions>
<PromptInputPressAction
icon={AudioWave01Icon}
tooltip="Transcribe"
aria-label="Transcribe"
/>
<PromptInputToggleAction
icon={RoboticIcon}
pressed={agent}
onPressedChange={setAgent}
tooltip="Agent mode"
aria-label="Agent mode"
/>
<PromptInputPopup>
<PromptInputPopupItem
icon={ZapIcon}
onSelect={({ close }) => close()}
>
Files
</PromptInputPopupItem>
<PromptInputPopupItem
icon={Upload01Icon}
onSelect={({ close, openFilePicker }) => {
openFilePicker();
close();
}}
>
Upload from device
</PromptInputPopupItem>
<PromptInputPopupItem
icon={Megaphone02Icon}
shortcut="@"
submenu
onSelect={({ close }) => close()}
>
Mention
</PromptInputPopupItem>
</PromptInputPopup>
</PromptInputActions>
</PromptInput>
</div>
);
}
Installation
Install the registry item into your project (same pattern as other Arrow UI components):
pnpm dlx @arrowui/cli@latest add promt-inputThis copies registry/promt-input.tsx and merges npm dependencies listed in the manifest. You still need shadcn-style UI primitives in your app (see Requirements).
Requirements
The component imports:
@/components/ui/popover— Radix Popover@/components/ui/tooltip— Radix Tooltip@/lib/utils—cn
Wrap your app (or subtree) with TooltipProvider from @/components/ui/tooltip if it is not already in your root layout. Tooltips are used for action buttons and the add (+) trigger.
Usage
import {
PromptInput,
PromptInputActions,
PromptInputPopup,
PromptInputPressAction,
} from "@/registry/promt-input";<PromptInput
placeholder="Ask anything…"
onSubmit={(value) => console.log(value)}
/>Variants
Default
Multiline field with scroll fades, top chrome strip, and avatar. Same state and handlers as small; layout differs.
Small
Single row: avatar, single-line textarea, actions, send.
"use client";
import { PromptInput } from "@/registry/promt-input";
export function PromptInputSmallDemo() {
return (
<div className="flex w-full max-w-199 justify-center">
<PromptInput variant="small" placeholder="Compact row…" />
</div>
);
}
<PromptInput variant="small" placeholder="…" />Streaming
While the assistant is streaming, set isLoading and wire onStop. The primary control switches from send (Arrow up) to Stop.
const [loading, setLoading] = useState(false);
<PromptInput
isLoading={loading}
onStop={() => setLoading(false)}
onSubmit={() => setLoading(true)}
/>- With
isLoading, Enter callsonStop(same as clicking Stop). - Send is disabled when there is nothing to send; Stop stays enabled so the user can always cancel.
Custom actions
Pass a single PromptInputActions child to PromptInput to replace the default action strip. Keep the same building blocks or compose your own.
<PromptInput onSubmit={…}>
<PromptInputActions>
<PromptInputPressAction
icon={AudioWave01Icon}
tooltip="Transcribe"
aria-label="Transcribe"
/>
<PromptInputPopup>
<PromptInputPopupItem onSelect={({ close }) => close()}>
Custom row
</PromptInputPopupItem>
</PromptInputPopup>
</PromptInputActions>
</PromptInput>The send button is always rendered after your actions (you do not include it inside PromptInputActions).
Add menu
The + button opens a popover. Menu content is children of PromptInputPopup. If you omit children, the panel is empty—define rows with PromptInputPopupItem.
PromptInputPopupItem: optionalicon, label (children), optionalshortcut(e.g."@"), optionalsubmenu(chevron only; no nested menu yet), optionaltrailing, andonSelectreceiving{ close, openFilePicker }.openFilePicker()opens the hidden file input (same as “upload” flows inside the default examples).
PromptInputAttachMenu is a deprecated alias of PromptInputPopup.
Hooks
usePromptInput()— must be used underPromptInput. Returns{ openFilePicker }for custom buttons that should open the file dialog.usePromptInputPopup()— must be used underPromptInputPopup(e.g. inside custom menu content). Returns{ close, openFilePicker }.
Dark mode
The component uses semantic Tailwind tokens (bg-card, text-foreground, muted, popover, gradients from background, etc.). No extra prop—respects .dark / your theme variables in globals.css.
Props
PromptInput
Pass children as PromptInputActions (single wrapper) to customize the action strip; otherwise built-in defaults (transcribe + PromptInputPopup) are used.
PromptInputPopup
PromptInputPopupItem
PromptInputPopupSelectPayload: { close: () => void; openFilePicker: () => void }.
PromptInputActions
Wrapper for the right-side action row. Accepts ComponentProps<"div"> (e.g. className, id). Renders with role="group" and data-slot="prompt-input-actions".