import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Thread,
Window,
} from "stream-chat-react";
const App = () => (
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<Thread />
</Channel>
);MessageComposer
MessageComposer provides the message-composer state, controls, and default UI for composing messages. It renders MessageComposerContext for input UI components and uses MessageComposerUI by default.
Best Practices
- Keep
MessageComposerunderChannelso composer state, permissions, and channel actions stay in sync. - Scope
WithComponentsaround the specificMessageComposerorThreadsubtree when one composer instance should differ. - Use
WithComponentsfor shared composer subcomponent overrides such asAttachmentSelector,LinkPreviewList, orAudioRecorder. - Prefer composer middleware or
overrideSubmitHandlerfor send-pipeline logic, not for layout customization. - Use
messageComposer.threadIdto detect thread composition instead of relying on deprecatedisThreadInput.
Basic Usage
UI Customization
MessageComposer renders the MessageComposerUI component from ComponentContext. That UI reads data from MessageComposerContext.
Replace the whole composer UI for one scoped subtree
import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Thread,
WithComponents,
Window,
} from "stream-chat-react";
const CustomInput = () => <div className="custom-input-shell">...</div>;
const App = () => (
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<WithComponents overrides={{ MessageComposerUI: CustomInput }}>
<MessageComposer />
</WithComponents>
</Window>
<Thread />
</Channel>
);Provide a shared input UI with WithComponents
import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Thread,
Window,
WithComponents,
} from "stream-chat-react";
const CustomInput = () => <div className="custom-input-shell">...</div>;
const App = () => (
<WithComponents overrides={{ MessageComposerUI: CustomInput }}>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<Thread />
</Channel>
</WithComponents>
);Props
| Prop | Description | Type |
|---|---|---|
additionalTextareaProps | Additional props forwarded to TextareaComposer. | Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "defaultValue" | "style" | "disabled" | "value"> |
asyncMessagesMultiSendEnabled | When enabled, recorded voice messages stay in the composer preview stack instead of being sent immediately after recording completes. Defaults to false. | boolean |
audioRecordingConfig | Custom recording configuration for voice messages. | CustomAudioRecordingConfig |
audioRecordingEnabled | Enables voice-message recording UI. Defaults to false. | boolean |
emojiSearchIndex | Custom emoji search implementation used by autocomplete and emoji replacement. | ComponentContextValue["emojiSearchIndex"] |
focus | Focuses the textarea on mount. Defaults to false. | boolean |
hideSendButton | Hides the default send button inside the current input UI. Defaults to false. | boolean |
maxRows | Maximum number of rows the default TextareaComposer can grow to. Defaults to 10. | number |
minRows | Minimum number of rows the default TextareaComposer starts with. | number |
overrideSubmitHandler | Overrides the default send flow for new messages. | (params: { cid: string; localMessage: LocalMessage; message: Message; sendOptions: SendMessageOptions; }) => Promise<void> | void |
parent | Parent message used when composing a thread reply. | LocalMessage |
shouldSubmit | Overrides the default submit shortcut. By default, Enter submits and Shift+Enter inserts a new line. | (event: React.KeyboardEvent<HTMLTextAreaElement>) => boolean |
Examples
Override the submit handler
import { MessageComposer, type MessageComposerProps } from "stream-chat-react";
const CustomMessageComposer = () => {
const overrideSubmitHandler: MessageComposerProps["overrideSubmitHandler"] =
async ({ cid, localMessage, message, sendOptions }) => {
// custom logic here
await sendMessageToBackend({ cid, localMessage, message, sendOptions });
};
return <MessageComposer overrideSubmitHandler={overrideSubmitHandler} />;
};sendMessageToBackend in the example above is app-owned code.