Message composer
Composes text and optional attachments with submission shortcuts and retained drafts on failure.
Preview
In action
When to use
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.
Install
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.
React
import { MessageComposer } from "@noorddev/vlak-react";
<MessageComposer onSend={async ({ text, files }) => sendMessage(text, files)} generating={generating} onStop={stopResponse} allowAttachments accept="image/*,.pdf" />Props
MessageComposer
A message draft with attachments, IME-safe shortcuts, and retained text after send failures.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | ||
defaultValue | string | "" | |
onValueChange | (text: string) => void | ||
onSendrequired | (message: ComposedMessage) => void | Promise<void> | ||
label | string | "Message" | |
placeholder | string | "Write a message…" | |
disabled | boolean | false | |
allowAttachments | boolean | false | |
accept | string | ||
maxLength | number | ||
sendOnEnter | boolean | false | Enter submits, Shift+Enter inserts a line. Otherwise use Cmd/Ctrl+Enter. |
generating | boolean | false | Application-owned response generation, separate from submission pending state. |
onStop | () => void | Requests that the application stop generation; does not itself cancel a network request. |
Also accepts Omit<FormHTMLAttributes<HTMLFormElement>, "defaultValue" | "onSubmit">.
Keyboard
| Keys | Does |
|---|---|
| Cmd+Enter, Ctrl+Enter | Submits the draft when no send or response generation is in progress. |
| Enter, Shift+Enter | With sendOnEnter enabled, Enter submits and Shift+Enter inserts a line; IME composition never submits. |
| Tab, Enter, Space | Operates attachment, send, and stop actions. |
Accessibility
- The textarea has a visible label and shortcut description.
- Sending prevents duplicate submission; results are announced.
- Only a successful send clears the draft and attachments; failures retain both.
- generating replaces Send with Stop response but keeps the next draft editable. onStop requests application cancellation; it does not cancel network activity itself.
Markup
<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>
Classes
.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