import {
Channel,
ChannelHeader,
MessageInput,
MessageList,
Thread,
Window,
WithComponents,
useMessageContext,
} from "stream-chat-react";
const CustomMessage = () => {
const { message } = useMessageContext();
return <div>{message.text}</div>;
};
const App = () => (
<WithComponents
overrides={{
Message: CustomMessage,
VirtualMessage: CustomMessage,
}}
>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
</WithComponents>
);This is beta documentation for Stream Chat React SDK v14. For the latest stable version, see the latest version (v13)
.
Message UI
The Message UI component consumes MessageContext and renders a message. The default implementation is MessageSimple.
Best Practices
- Start by composing the exported message subcomponents instead of rebuilding the whole surface.
- Use
useMessageContext()inside custom message UI. - Set shared defaults with
WithComponentsand use list-level overrides only when the customization is local. - Keep channel and thread message UIs consistent unless the product intentionally treats them differently.
- Preserve the default message layout semantics for accessibility and keyboard behavior.
Basic Usage
To set a shared default message UI for a Channel subtree, register it with WithComponents:
If you only want to customize one list, use the list-level Message prop:
import { MessageList, Thread, VirtualizedMessageList } from "stream-chat-react";
<MessageList Message={CustomMessage} />;
<Thread Message={CustomMessage} />;
<VirtualizedMessageList Message={CustomMessage} />;UI Customization
MessageSimple is built from smaller UI pieces that each consume MessageContext. A custom message UI often looks like a variation of this structure:
import {
Attachment,
MessageActions,
MessageRepliesCountButton,
MessageStatus,
MessageText,
MessageTimestamp,
Poll,
QuotedMessage,
ReactionsList,
useChatContext,
useMessageContext,
} from "stream-chat-react";
const CustomMessage = () => {
const { client } = useChatContext();
const { handleAction, handleOpenThread, message } = useMessageContext();
const poll = message.poll_id ? client.polls.fromState(message.poll_id) : null;
return (
<div className="custom-message">
<MessageActions />
{poll && <Poll poll={poll} />}
{message.quoted_message && <QuotedMessage />}
{message.attachments?.length ? (
<Attachment
actionHandler={handleAction}
attachments={message.attachments}
/>
) : null}
<MessageText />
<ReactionsList />
{!!message.reply_count && (
<MessageRepliesCountButton
onClick={handleOpenThread}
reply_count={message.reply_count}
thread_participants={message.thread_participants}
/>
)}
<MessageTimestamp />
<MessageStatus />
</div>
);
};Other exported building blocks such as MessageDeletedBubble, MessageBlocked, MessageTranslationIndicator, ReminderNotification, MessageAlsoSentInChannelIndicator, and StreamedMessageText can be composed the same way.
Current Override Paths
- Use
WithComponents overrides={{ Message: CustomMessage }}to set the default non-virtualized message UI for a subtree. - Use
WithComponents overrides={{ VirtualMessage: CustomMessage }}to set the default virtualized message UI for a subtree. - Use
<MessageList Message={CustomMessage} />,<Thread Message={CustomMessage} />, or<VirtualizedMessageList Message={CustomMessage} />for local overrides. - Use
MessageActionsto customize the action menu and quick actions.
Props
| Prop | Description | Type |
|---|---|---|
partial MessageContextValue surface | Custom message UI components can receive a partial MessageContextValue prop surface, but the recommended approach is to call useMessageContext() directly. That keeps the component aligned with the current SDK data flow and avoids relying on which values are injected by the caller. | Partial<MessageContextValue> |