Skip to content
Sign in
ComposerPill composerMenu

Pill composerPro

A compact pill that opens into the full composer when it gets focus, growing out of the pill's own shape, and folds back with the draft kept.

ProA trip planner where the thread gets the room: the pill opens into the composer, sends, and folds back while the reply streams.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/pill-composer

Usage

tsx
import { PillComposer } from "@/components/ui/chatcn/pro/pill-composer"

<PillComposer
  placeholder="Ask about this trip…"
  status={status}
  onStop={stop}
  onSubmit={({ text }) => send(text)}
  tools={<PromptInputAttachButton />}
/>

The open composer is laid out at full size all the time; closed, a clip path cuts it down to the pill. Opening animates that clip from the pill's rectangle and full radius to the card's, while the pill's words glide to where the caret lands, so it reads as one object growing rather than two swapping. Only the pill's 44 px are reserved: with anchor="bottom" the composer grows upward over the thread, with top it grows down. Closing keeps the draft and shows it in the pill. While a reply streams the pill says so and carries the Stop button. Everything else is the free PromptInput: IME safe Enter, autosize, paste and drop, the same props.

Examples

States

ProClosed, a kept draft, open, writing a reply with Stop in the pill, grown from the top, and disabled.

Props

PropTypeNotes
value, defaultValue, onValueChangestringThe draft, controlled or not
onSubmit({ text }) => voidEnter or Send. Never while empty, disabled or busy
status, onStop"idle" | "submitting" | "streaming", () => voidSend becomes Stop in the same footprint
disabledboolean
open, defaultOpen, onOpenChangebooleanFocus on the pill opens it
anchor"bottom" | "top"The edge it grows from. Default bottom
align"center" | "start"Where the pill sits. Default center
pillWidthstringDefault min(100%, 20rem)
tools, actions, attachmentsReactNodeToolbar start, before Send, above the text
hotkeystring | nullOpens it from anywhere. Default /
iconReactNodeLeading icon in the pill
labelsPartial<PillComposerLabels>Every string, for translation
...PromptInputPropsonFiles, accept, submitOn and the rest pass through

Keyboard

KeyAction
Tab to the pillOpens the composer and moves focus into it
/Opens the composer from anywhere outside a text field
Enter, Shift EnterSend, new line. Never sends while an IME is composing
EscapeStops a streaming reply first, then folds the composer back, keeping the draft

Accessibility

  • The pill is a button with aria-expanded; its name includes the kept draft.
  • While closed the composer is inert, so nothing hidden is reachable.
  • Closing with Escape hands focus back to the pill without reopening it.
  • Menus and popovers opened from the toolbar count as inside, so using them never folds the composer.

Motion

The outline morphs in 280 ms and folds back in 210 ms on the kit's ease out, animating only clip-path; contents fade in 200 ms after a 60 ms beat. The edge and shadow are a drop shadow on the clipped shape, so they follow the morph. Reduced motion snaps the outline and keeps the fades.