import { useComponentContext } from "stream-chat-react";
export const CustomMessageListHeader = () => {
const { EmptyStateIndicator, MessageActions } = useComponentContext();
return (
<div>
<span>
{EmptyStateIndicator ? "empty state available" : "no override"}
</span>
<span>
{MessageActions ? "custom actions registered" : "default actions"}
</span>
</div>
);
};ComponentContext
ComponentContext holds the React Chat SDK UI override surface for a Channel subtree. Read it with useComponentContext(). Register overrides with WithComponents.
Best Practices
- Scope overrides with
WithComponentsas close as possible to the subtree that needs them. - Prefer wrapping or lightly extending default SDK components before replacing whole surfaces.
- Keep custom message UIs based on
useMessageContext()and related hooks, not on assumed injected props. - Use local component props only when that component still exposes a supported prop surface.
- Keep your override map documented, because most SDK UI customization now flows through
ComponentContext.
Basic Usage
Read values from the context inside any Channel child:
Registering Overrides With WithComponents
WithComponents merges the nearest override set into the existing ComponentContext. Inner wrappers win over outer wrappers.
import {
Channel,
ChannelHeader,
MessageInput,
MessageList,
Thread,
Window,
WithComponents,
} from "stream-chat-react";
export const App = () => {
return (
<WithComponents
overrides={{
Input: CustomMessageInput,
Message: CustomMessage,
MessageActions: CustomMessageActions,
}}
>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
</WithComponents>
);
};You can scope overrides more narrowly too:
import {
Channel,
MessageInput,
MessageList,
Thread,
Window,
WithComponents,
} from "stream-chat-react";
export const App = () => {
return (
<WithComponents overrides={{ Input: MainChannelInput }}>
<Channel>
<Window>
<MessageList />
<MessageInput />
</Window>
<WithComponents overrides={{ Input: ThreadInputOverride }}>
<Thread />
</WithComponents>
</Channel>
</WithComponents>
);
};Message and VirtualMessage overrides should read SDK state from hooks like useMessageContext() instead of assuming the SDK injects message UI props directly.
Current Override Keys
The current ComponentContext value includes the following keys.
Composer And Input
| Key | Use for |
|---|---|
AdditionalMessageComposerActions | extra composer action buttons next to the textarea |
AttachmentPreviewList | the shared preview list in MessageInput |
AttachmentSelector | the attachment selector UI |
AttachmentSelectorInitiationButtonContents | the selector button contents |
AudioRecorder | the voice-recording composer surface |
AutocompleteSuggestionItem | individual suggestion-list items |
AutocompleteSuggestionList | the suggestion-list container |
CommandChip | the selected command chip |
CooldownTimer | the slow-mode cooldown UI |
EditedMessagePreview | the edit-preview row shown by the composer |
EmojiPicker | the emoji picker trigger and picker UI |
FileDragAndDropContent | the drag-and-drop overlay content |
ImagePlaceholder | the failed-image placeholder UI |
Input | the high-level message input UI |
LinkPreviewList | link previews inside the composer preview stack |
RecordingPermissionDeniedNotification | the recorder permission-denied UI |
SendButton | the send button |
SendToChannelCheckbox | the thread reply "also send to channel" checkbox |
ShareLocationDialog | the location-sharing dialog UI |
StartRecordingAudioButton | the audio-recording start button |
StopAIGenerationButton | the AI generation stop button in composer actions |
TextareaComposer | the text input control |
VoiceRecordingPreviewSlot | the dedicated voice-recording preview slot above attachments |
emojiSearchIndex | custom emoji search implementation for the composer |
Message, Notifications, And Lists
| Key | Use for |
|---|---|
Attachment | message attachment rendering |
DateSeparator | date separators in message lists |
EmptyStateIndicator | empty list or empty thread placeholders |
GiphyPreviewMessage | giphy preview rows in message lists |
HeaderComponent | content rendered above MessageList |
LoadingErrorIndicator | loading error UI |
LoadingIndicator | loading UI while querying or paginating |
Message | the default non-virtualized message renderer |
MessageActions | the message actions surface |
MessageAlsoSentInChannelIndicator | the thread "also sent in channel" indicator |
MessageBlocked | moderation-blocked message UI |
MessageBouncePrompt | bounced message prompt content |
MessageDeleted | alternate deleted-message renderer with no default |
MessageDeletedBubble | deleted message bubble renderer |
MessageEditedIndicator | Custom UI component to display the "Edited" label and tooltip when a message has been edited |
MessageListItem | wrapper for each list item |
MessageListMainPanel | the main panel around the message list |
MessageListNotifications | message-list notifications container |
MessageListWrapper | wrapper around the list contents |
MessageRepliesCountButton | replies-count button |
MessageReactionsDetail | reaction detail dialog content |
MessageStatus | message delivery and read status |
MessageSystem | system message rendering |
MessageTimestamp | per-message timestamp UI |
MessageTranslationIndicator | translation indicator UI |
NewMessageNotification | the "new messages" notification |
NotificationList | the client-notification list UI |
PinIndicator | pinned-message indicator |
QuotedMessage | quoted message rendering in message bubbles |
ReminderNotification | reminder UI in messages |
StreamedMessageText | streamed AI text rendering |
TypingIndicator | the message-list typing indicator |
UnreadMessagesNotification | the unread notification while scrolled away |
UnreadMessagesSeparator | the unread separator row |
VirtualMessage | the virtualized message renderer |
Reactions, Polls, And Media
| Key | Use for |
|---|---|
Avatar | single-user avatar rendering |
AvatarStack | stacked avatar rendering |
BaseImage | low-level image rendering and fallbacks |
Gallery | gallery carousel UI |
Modal | the modal shell used by SDK features |
ModalGallery | gallery viewer content |
PollActions | poll actions renderer |
PollContent | poll body renderer |
PollCreationDialog | poll creation dialog contents |
PollHeader | poll header renderer |
PollOptionSelector | poll option selector UI |
QuotedMessagePreview | quoted-message preview inside the composer |
ReactionSelector | the reaction selector UI |
ReactionsList | the inline reactions list |
VideoPlayer | video playback UI |
reactionOptions | shared reaction options for selector and list components |
Channel Preview, Thread, Search, And Shared Utility UI
| Key | Use for |
|---|---|
CalloutDialog | callout-dialog contents |
ChannelPreviewActionButtons | action buttons inside ChannelPreviewMessenger |
Search | the main search UI |
SearchBar | the search input area |
SearchResults | the results pane |
SearchResultsHeader | results header content |
SearchResultsPresearch | the pre-query state for results |
SearchSourceResultList | the result-list UI for one source |
SearchSourceResultListFooter | end-of-list footer for one source |
SearchSourceResults | one source section in search results |
SearchSourceResultsEmpty | empty state for one source |
SearchSourceResultsHeader | header for one source section |
SearchSourceResultsLoadingIndicator | loading state for one source section |
ThreadHead | thread parent/header message at the top of thread lists |
ThreadHeader | thread header UI |
ThreadInput | thread message input UI |
ThreadListEmptyPlaceholder | empty state in thread lists |
ThreadListItem | thread list item wrapper |
ThreadListItemUI | thread list item UI |
ThreadListLoadingIndicator | thread list loading UI |
ThreadListUnseenThreadsBanner | unseen-thread banner |
ThreadStart | thread-start marker or content |
Timestamp | shared timestamp component used by other UI |
Choosing Between Props And Context
Use the local component prop when that component still exposes a supported prop surface. Use WithComponents and ComponentContext when you want to replace SDK-owned UI deeper in the tree.
Examples:
- use
<MessageInput Input={CustomInput} />when customizing one specificMessageInputinstance - use
<WithComponents overrides={{ Input: CustomInput }}>when you want every nested SDK input to use the same override - use
<WithComponents overrides={{ MessageActions: CustomMessageActions }}>when you want to replace the default message action UI