Skip to main content

PromptInput

Composerai

Chat composer FORM (Enter submits) emitting a message to the app's runtime.

import { PromptInput } from "@elabs-ai/components-ai";
Install
npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-ai

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.

Add the brand-ui PromptInput component (from @elabs-ai/components-ai) to this project.

1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`.
2. Look PromptInput up before writing code: `npx -y @elabs-ai/components-cli docs PromptInput` prints its real props, variants, what it pairs with and what to avoid.
3. If @elabs-ai/components-ai is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-ai` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-ai`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`.
4. Use it here: [the screen or file, and what it should show]
5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop.
6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.

Avoid

  • Treating PromptInput as a controlled textarea — it is a form; use onSubmit, not value/onChange plumbing.
  • Submitting model calls from the component — emit the message; the app owns transport.
  • Passing children to PromptInputSubmit for every status — the Stop affordance goes invisible while generating; pass it for the resting state only.
  • Expecting className to land on the visible well — className styles the outer <form>; use surfaceClassName to reach the well.

PromptInput — the raw composer FORM primitive Composer is built on. Compose PromptInputBody / PromptInputTextarea / PromptInputFooter / PromptInputTools / PromptInputSubmit yourself when you need a bespoke shell; reach for <Composer /> for the standard chat input. tone picks the inner well's fill: "surface" (default, muted — the look every existing usage keeps) or "card", letting the well nest inside an already-tinted outer frame — the "double card" that PromptInput could not express before this prop existed. The card well's fill is theme-driven, not universally "white" — raised (lighter than the outer frame) on light themes, recessed (darker) on dark; see the tone prop doc.

Works with

Contains

  • PromptInputBody
  • PromptInputTextarea
  • PromptInputFooter
  • PromptInputSubmit

Examples

7 live examples

Double Card Toned

The "double card" (#254): a tinted `bg-surface-muted` outer frame around a `tone="card"` well. Raised (lighter than the frame) on light themes, recessed (darker) on dark — check both themes here, not just the light default.

Idle

At rest: Send, disabled until there is text or an attachment.

Running Empty

Running, composer empty — the control IS the Stop affordance (unchanged).

Running With Draft

Running, but the user has typed a follow-up — the P0 fix. The control flips back to Send so the draft can always be submitted mid-turn; brand-ui never asserts what `onSubmit` does with it (queue, interleave — the app decides).

Error State

Failed — an X that stays clickable so the user can retry.

Separate Stop Control

The composed "separate" arrangement: a dedicated `PromptInputStop` beside `PromptInputSubmit`. Never a `mode` prop (component-api.md bans behavioural modes) — compose the two controls instead. While `PromptInputStop` is mounted, `PromptInputSubmit` stays Send in every state; the dedicated control owns stopping.

API

PromptInput

PropTypeDescription
acceptstringe.g., "image/*" or leave undefined for any
multipleboolean
globalDropbooleanWhen true, accepts drops anywhere on document. Default false (opt-in).
syncHiddenInputbooleanRender a hidden input with given name and keep it in sync for native form posts. Default false.
maxFilesnumberMinimal constraints
maxFileSizenumberbytes
tone"surface" | "card"The fill of the inner well (the `InputGroup` `variant`). `"surface"` (default) is the standard muted composer look; `"card"` renders a `bg-card` well for nesting inside an already-tinted outer frame — the "double card" look (#254). e.g. `<div className="rounded-xl bg-surface-muted p-1.5"><PromptInput tone="card">…</PromptInput></div>`. The well fill is NOT universally "white": `--card` reads lighter (raised) than `--surface-muted` on light themes, but darker (recessed) on dark — the same theme-dependent trade-off documented for `bg-surface-muted`-as-a-well in `styling-and-tokens.md`. The well stays a legible, distinct tone against the outer frame in every theme; only the "raised" framing is light-themes-specific.
onError(err: { code: "max_files" | "max_file_size" | "accept"; message: string }) => void
onSubmitrequired( message: PromptInputMessage, event: FormEvent<HTMLFormElement>, ) => void | Promise<void>
surfaceClassNamestringClasses for the inner surface (the visible `InputGroup` well), e.g. to shape its corners. `className` styles the outer `<form>`; this styles the well — the only way to reach it from outside (used by `Composer`).

Also accepts: Omit<HTMLAttributes<HTMLFormElement>, "onSubmit" | "onError">

PromptInputActionAddAttachments

PropTypeDescription
labelstring

Also accepts: ComponentProps<typeof DropdownMenuItem>

PromptInputActionAddScreenshot

PropTypeDescription
labelstring

Also accepts: ComponentProps<typeof DropdownMenuItem>

PromptInputActionMenu

Also accepts: ComponentProps<typeof DropdownMenu>

PromptInputActionMenuContent

Also accepts: ComponentProps<typeof DropdownMenuContent>

PromptInputActionMenuItem

Also accepts: ComponentProps<typeof DropdownMenuItem>

PromptInputActionMenuTrigger

PropTypeDescription
tooltipPromptInputButtonTooltip

Also accepts: PromptInputButtonProps

PromptInputBody

Also accepts: HTMLAttributes<HTMLDivElement>

PromptInputButton

PropTypeDescription
tooltipPromptInputButtonTooltip

Also accepts: ComponentProps<typeof InputGroupButton>

PromptInputCommand

Also accepts: ComponentProps<typeof Command>

PromptInputCommandEmpty

Also accepts: ComponentProps<typeof CommandEmpty>

PromptInputCommandGroup

Also accepts: ComponentProps<typeof CommandGroup>

PromptInputCommandInput

Also accepts: ComponentProps<typeof CommandInput>

PromptInputCommandItem

Also accepts: ComponentProps<typeof CommandItem>

Theming

The tokens each state resolves to. Change the token and every theme follows.

StateToken
submitstatus=ready|submitted|streaming|error