Skip to content
Sign in
MarketingHero composerMenu

Hero composerPro

A landing hero whose main object is the composer itself: example prompts take turns in the placeholder, files attach, the model chip opens a menu and Send routes to your app.

ProA landing hero whose main object is the composer: example prompts take turns in the placeholder, files attach, the model chip opens a menu and Send routes to a callback.

Installation

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

Usage

tsx
import { HeroComposer } from "@/components/ui/chatcn/pro/hero-composer"

<HeroComposer
  title="Ask the whole company one question."
  examples={["Why did weekly active teams drop last month…", "Draft the customer note…"]}
  models={[{ id: "m2", label: "Claude Sonnet 5.5" }]}
  onSubmit={({ text, model }) => router.push(`/new?q=${encodeURIComponent(text)}&m=${model}`)}
/>

The visitor can start typing at once. While the field is empty and not focused, examples take turns in the placeholder: the current one moves up and out, the next comes in from below, with no letters typing in. The same examples show as chips under the composer; a chip fills the field and puts the caret at the end. Rotation holds while the field has focus or text, and stops offscreen.

onSubmit gets { text, model, files }. Route to your app with the text in the URL, or keep it through sign up, so the first message survives the trip. Pass status="submitting" while you route. Enter sends, Shift+Enter adds a line, and nothing sends during IME composition.

Every section is full width with a 16 px gutter on phones and 32 px from 768 px, content capped at 1200 px. Layout follows the section's own width through a container query, not the viewport, so a section in a narrow column lays out like a phone.

Examples

States

Submitting with the text still in place, disabled during maintenance, a single model without examples, and a right to left page.

ProSubmitting, disabled, and a single model without examples.

Props

PropTypeNotes
titleReactNodeRequired. One idea; it balances over two or three lines
descriptionReactNodeThe paragraph under or beside the headline
examplesstring[]Example prompts, in the placeholder and as chips
intervalnumberMs each example stays. Default 3200
examplesLabelstringBefore the chips. Default "Try"
defaultValuestringText already in the field, for example from the URL
placeholderstringWithout examples. Default "Ask anything…"
inputLabelstringAccessible name of the field. Default "Message"
models, model, defaultModel, onModelChange{ id, label, description? }[]One model shows as a plain label, two or more as a menu, none hides the chip
onSubmit({ text, model, files }) => voidNot called while empty or disabled
status"idle" | "submitting"Spinner in the send button while you route
disabledbooleanThe field, chips and model chip stop taking input
attachments, accept, maxFilesThe attach row. Default on, 5 files
footnoteReactNodeA line under the composer, for example the free tier
childrenReactNodeMedia or a small live object under everything

Keyboard

KeyAction
EnterSends. Never during IME composition
Shift+EnterNew line
TabMoves to attach, the model chip, send and the example chips
Enter / Space on a chipFills the field with that example

Accessibility

  • The section is labelled by its headline. Heroes use an h1, other sections an h2; render one hero per page.
  • The rotating placeholder is aria-hidden; the chips carry the same examples as real buttons, so nothing depends on watching the swap.
  • The model chip is a menu button named Model: <name>, with a radio group inside.
  • Every control is at least 32 px, 44 px on touch screens.

Motion

  • The incoming example rises 0.45 em and fades in over 280 ms; the outgoing one leaves upward over 210 ms. Both are transitions, so a fast click through the chips never stacks animations.
  • Under reduced motion the swap is a 150 ms crossfade with no movement.
  • Chips press to 0.97 over 100 ms and never scale on hover.