Skip to content

ChatComposer

ChatComposer

const [pending, setPending] = useState(false);
<ChatComposer
isSending={pending}
beforeInput={<IconButton aria-label="Attach"><PaperclipIcon /></IconButton>}
attachments={files.map((f) => <AttachmentTile key={f.id} name={f.name} size={f.size} onRemove={() => remove(f.id)} />)}
onSend={async (text) => {
setPending(true);
await send(text);
setPending(false);
}}
/>

Install: @helixui/core

import { ChatComposer } from '@helixui/core'

status: stable · since: 0.3.0

Tags: input, message, send, ai, multiline

Anatomy

┌────────────────────────────────────────┐
│ [📎] | Type a message... [↑] │
│ (auto-resize, Enter sends) │
└────────────────────────────────────────┘

Layout

  • displayblock
  • widthfill
  • heightcontent
  • intrinsicSizemin ~48px, grows up to maxHeight on multi-line
  • stackablefalse
  • fullBleedfalse

Visual

A rounded surface (radius.lg) holding a multi-line textarea, optional attachment button on the left, and a brand-colored send button on the right. Auto-grows up to a max height, then scrolls inside. Enter sends; Shift+Enter inserts a newline.

Props

NameTypeDefaultDescription
valuestringControlled message text.
defaultValuestringUncontrolled initial text.
onChange(v: string) => voidCalled on every keystroke.
onSend(v: string) => voidCalled when the user presses Enter or clicks send. Receives the trimmed message.
placeholderstringSend a message…Placeholder.
isDisabledbooleanfalseDisable the entire composer.
isSendingbooleanfalseWhile true, the send button is disabled (use during request).
attachmentsReactNodeSlot above the input — attachment chips, mentions.
beforeInputReactNodeSlot to the left of the input (formatting toolbar, attach button).
afterInputReactNodeSlot to the right of the input, before the send button.
sendIconReactNodeOverride the default paper-plane icon.
sendLabelstringSendaria-label on the send button.
minRowsnumber1Auto-resize floor.
maxRowsnumber8Auto-resize ceiling; scrolls beyond.

Tokens used

color.bg.surface.default, color.bg.surface.muted, color.bg.action.brand.default, color.bg.action.brand.hover, color.bg.action.neutral.default, color.text.primary, color.text.on.brand, color.text.muted, color.border.default, color.border.focus, radius.lg, radius.full, space.1, space.2, space.3, font.family.sans, font.size.md

Accessibility

Keyboard

KeyAction
EnterSend the message.
Shift+EnterInsert newline.

Notes

  • The send button is disabled when the message is empty or whitespace.
  • IME composition is respected — Enter does not send while composing (Korean/Japanese/Chinese input).

Composes with

ComponentRelationNote
AttachmentTilechildRenders attached files inside the composer.
PromptSuggestionssiblingSuggestions appear above the composer.
ChatListsiblingSits below the chat list scroll area.
ChatMessagesibling
IconButtonsibling

Prompt examples

These are the AI prompt → JSX mappings used by the helixui prompt DSL and integrations like Cursor / Claude Code.

AI chat input

“composer that sends on Enter”

<ChatComposer onSend={handleSend} placeholder="Ask anything…" />

with attachments

“composer with paperclip”

<ChatComposer onSend={handleSend} onAttach={handleAttach} />