Thread composer
FreeA compact reply box for comments and threads, with send and cancel built in.
pnpm dlx shadcn@latest add https://caliperui.site/r/thread-composer.jsonEnter to send · Shift+Enter for a new line
When to use #
Use it for
- Replying inside a comment thread or a record's activity feed.
- You want Enter to send and Shift+Enter to add a line.
Skip it when
- The message is a long document; use the rich text editor.
- It is the main input of an assistant; use the prompt input.
Installation #
terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/thread-composer.json
Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
example.tsx
import { useState } from "react" import { ThreadComposer } from "@/components/ui/thread-composer" export function ThreadComposerDemo() { const [sent, setSent] = useState<string[]>([]); return ( <div className="grid max-w-xl gap-3"> <ThreadComposer label="Reply" placeholder="Write a reply" onSubmit={(text) => setSent((all) => [...all, text])} /> <ul className="grid gap-1 text-sm text-muted-foreground" aria-live="polite"> {sent.map((text, i) => <li key={i}>{text}</li>)} </ul> </div> ); }
API reference #
ThreadComposer
component
PropTypeDefaultDescription
label *string—
onSubmit *(text: string) => void—
onCancel(() => void)—
placeholderstring—
submitLabelstring"Send"
submitOn"enter" | "mod-enter""enter""enter" sends on Enter and breaks lines on Shift+Enter; "mod-enter" needs Ctrl or Cmd.
defaultValuestring""
autoFocusboolean—
disabledboolean—
maxRowsnumber6
classNamestring—
Accessibility #
It is built on native elements and keeps aria-label, aria-describedby and aria-hidden in step with state.
KeyBehaviour
EscapeCloses or cancels, then returns focus to where it came from.
EnterActivates the focused control.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.