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>
);Input UI
The Input UI component consumes MessageComposerContext and renders the visible composer. The default implementation is MessageComposerUI.
Best Practices
- Start from the exported building blocks before replacing the whole composer.
- Scope
WithComponentsaround a specific composer subtree when only one layout should differ. - Use
WithComponentsfor shared subcomponent overrides. - Keep the preview stack and textarea together so autocomplete, uploads, and recording state remain coherent.
- If you want the default slash-command UX, keep
CommandChipnext toTextareaComposerand hide attachment or extra-action controls while a command is selected. - Reuse the default
str-chat__message-composer*classes if you want to keep the built-in styling.
Basic Usage
Use WithComponents and the MessageComposerUI override key to replace the whole composer UI for a specific subtree.
UI Customization
MessageComposerUI is a good reference for a custom composer. In v14 the default structure is:
AttachmentSelector- preview stack:
EditedMessagePrevieworQuotedMessagePreviewVoiceRecordingPreviewSlotAttachmentPreviewListLinkPreviewList
- text and action controls:
CommandChipTextareaComposerSendToChannelCheckboxAdditionalMessageComposerActionsMessageComposerActions
A simplified custom input can keep the same composition model:
import {
AttachmentPreviewList,
AttachmentSelector,
LinkPreviewList,
MessageComposerActions,
MessageComposer,
QuotedMessagePreview,
SendToChannelCheckbox,
TextareaComposer,
VoiceRecordingPreviewSlot,
WithDragAndDropUpload,
} from "stream-chat-react";
const CustomInput = () => (
<WithDragAndDropUpload
className="str-chat__message-composer-container"
component="div"
>
<div className="str-chat__message-composer">
<AttachmentSelector />
<div className="str-chat__message-composer-compose-area">
<div className="str-chat__message-composer-previews">
<QuotedMessagePreview />
<VoiceRecordingPreviewSlot />
<AttachmentPreviewList />
<LinkPreviewList />
</div>
<div className="str-chat__message-composer-controls">
<TextareaComposer />
<SendToChannelCheckbox />
<MessageComposerActions />
</div>
</div>
</div>
</WithDragAndDropUpload>
);
const Composer = () => (
<WithComponents overrides={{ MessageComposerUI: CustomInput }}>
<MessageComposer />
</WithComponents>
);When a slash command is selected, the default MessageComposerUI adds str-chat__message-composer--command-active, keeps the selected command visible through CommandChip, and collapses both AttachmentSelector and AdditionalMessageComposerActions. Mirror that behavior in your own layout only if you want parity with the default composer.
For a shared override across the app, register the input UI with WithComponents:
import { Channel, MessageComposer, WithComponents } from "stream-chat-react";
const CustomInput = () => <div className="custom-input-shell">...</div>;
const App = () => (
<WithComponents overrides={{ MessageComposerUI: CustomInput }}>
<Channel>
<MessageComposer />
</Channel>
</WithComponents>
);Props
| Prop | Description | Type |
|---|---|---|
| none | The Input UI component itself does not receive required props. It should read the composer state it needs from useMessageComposerContext(), useMessageComposerController(), and the exported message-composer hooks. | - |