Message composer

Composes text and optional attachments with submission shortcuts and retained drafts on failure.

Cmd or Ctrl+Enter to send.

Continue the conversation

Cmd or Ctrl+Enter to send.

Use it for

  • Chat, comments, or a support reply.
  • Async submission that must preserve a draft when it fails.
  • Application-owned response generation with a Stop response action.

Not for

  • An arbitrary multi-field form or uploading files without a message.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { MessageComposer } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/message-composer.

2. Vendor the source

npx @noorddev/vlak-cli add message-composer

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/message-composer.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { MessageComposer } from "@noorddev/vlak-react";

<MessageComposer onSend={async ({ text, files }) => sendMessage(text, files)} generating={generating} onStop={stopResponse} allowAttachments accept="image/*,.pdf" />

MessageComposer

A message draft with attachments, IME-safe shortcuts, and retained text after send failures.

PropTypeDefaultDescription
valuestring
defaultValuestring""
onValueChange(text: string) => void
onSendrequired(message: ComposedMessage) => void | Promise<void>
labelstring"Message"
placeholderstring"Write a message…"
disabledbooleanfalse
allowAttachmentsbooleanfalse
acceptstring
maxLengthnumber
sendOnEnterbooleanfalseEnter submits, Shift+Enter inserts a line. Otherwise use Cmd/Ctrl+Enter.
generatingbooleanfalseApplication-owned response generation, separate from submission pending state.
onStop() => voidRequests that the application stop generation; does not itself cancel a network request.

Also accepts Omit<FormHTMLAttributes<HTMLFormElement>, "defaultValue" | "onSubmit">.

KeysDoes
Cmd+Enter, Ctrl+EnterSubmits the draft when no send or response generation is in progress.
Enter, Shift+EnterWith sendOnEnter enabled, Enter submits and Shift+Enter inserts a line; IME composition never submits.
Tab, Enter, SpaceOperates attachment, send, and stop actions.
<form class="rs-message-composer" aria-label="Message"><label>Message<textarea class="rs-textarea" placeholder="Write a message…"></textarea></label><div class="rs-message-composer-actions"><button class="rs-btn-primary" type="submit">Send</button></div></form>
.rs-message-composer.rs-message-composer-actions.rs-message-composer-action.rs-message-composer-files.rs-message-composer-file.rs-message-composer-hint.rs-message-composer-input