ComponentContext
ComponentContext holds the override keys: for each replaceable part of the UI, the component to render instead of the SDK's. Set keys with WithComponents, and read them with useComponentContext(). Overriding components explains how overrides apply and lists the keys by area of the screen. This page lists what each key's component receives and what renders by default.
Best practices
- Declare the
overridesobject once, at module scope or withuseMemo. A new object on every render re-renders every component that reads the context, each message included. - Put
WithComponentsabove the component that reads the key.ChatreadsNotificationAnnouncer, so that key only works from aWithComponentsaroundChat. - Use
WithComponents, which merges with the overrides above it.ComponentProviderreplaces them all, so use it only to reset a subtree to the defaults. - A custom
MessageUIorVirtualMessagereceives no props. It reads the message withuseMessageContext(). See A custom MessageUI. - Before rebuilding a part, check whether the default takes what you need. For example, to change the message actions, render
MessageActionswith your ownmessageActionSet, as Message actions shows.
Basic usage
The SDK reads a key and falls back to its own component. A component of yours can do the same, so it shows whatever the app registered:
import {
LoadingIndicator as DefaultLoadingIndicator,
useComponentContext,
} from "stream-chat-react";
export const MembersLoading = () => {
const { LoadingIndicator = DefaultLoadingIndicator } = useComponentContext();
return <LoadingIndicator />;
};useComponentContext() returns {} outside every provider. It never throws.
Registering overrides with WithComponents
WithComponents merges its overrides into the context from above. A nested one adds its keys and keeps the rest:
import type {
Channel as StreamChannel,
Thread as StreamThread,
} from "stream-chat";
import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Thread,
ThreadHeader,
WithComponents,
} from "stream-chat-react";
import type { DateSeparatorProps } from "stream-chat-react";
const DayOnlySeparator = ({ date }: DateSeparatorProps) => (
<div className="str-chat__date-separator">{date.toLocaleDateString()}</div>
);
const ClosedReplies = () => (
<div className="closed-replies">Replies are closed.</div>
);
const appOverrides = { DateSeparator: DayOnlySeparator };
const threadOverrides = { MessageComposerUI: ClosedReplies };
type ConversationProps = {
channel: StreamChannel;
thread?: StreamThread;
};
export const Conversation = ({ channel, thread }: ConversationProps) => (
<WithComponents overrides={appOverrides}>
<Channel channel={channel}>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Channel>
{thread && (
<WithComponents overrides={threadOverrides}>
<Thread thread={thread}>
<ThreadHeader />
<MessageList />
<MessageComposer />
</Thread>
</WithComponents>
)}
</WithComponents>
);Both message lists render DayOnlySeparator. Only the thread's composer renders ClosedReplies. The icons key merges icon by icon, so a nested WithComponents that sets one icon keeps the icons set above it.
Current override keys
"none" in the props column means the component receives no props: it reads what it needs from context. Where the default takes props of its own, the SDK still passes none: an override that renders the default can pass them. A default marked "built in" is a component the SDK doesn't export.
| Key | Replaces | Props | Default |
|---|---|---|---|
AdditionalMessageComposerActions |
the area next to the composer's input that holds the emoji picker | none | AdditionalMessageComposerActions |
Attachment |
a message's attachments | AttachmentProps |
Attachment |
AttachmentFileIcon |
the file-type icon on file attachments | FileIconProps |
FileIcon |
AttachmentPreviewList |
the attachment previews in the composer | none; the default takes AttachmentPreviewListProps |
AttachmentPreviewList |
AttachmentSelector |
the composer's attachment button and its menu | none | AttachmentSelector, and SimpleAttachmentSelector while editing a message |
AttachmentSelectorInitiationButtonContents |
the content of the attachment button | none | the plus icon |
AttachmentUploadProgressIndicator |
the progress of a message attachment that is still uploading | AttachmentUploadProgressIndicatorProps |
AttachmentUploadProgressIndicator |
AudioRecorder |
the voice recorder in the composer | none | AudioRecorder |
AutocompleteSuggestionItem |
one item of the composer's suggestion list | SuggestionItemProps |
SuggestionListItem |
AutocompleteSuggestionList |
the composer's suggestion list | SuggestionListProps |
SuggestionList |
Avatar |
a user's avatar | ChannelAvatarProps |
Avatar, and ChannelAvatar in channel-list rows, channel management and member details |
AvatarStack |
a row of overlapping avatars | AvatarStackProps |
AvatarStack |
BaseImage |
an image, with its fallback when it fails to load | BaseImageProps |
BaseImage |
CalloutDialog |
the content of a callout dialog | CalloutDialogProps |
built in |
ChannelListHeader |
the channel list's header | none | ChannelListHeader |
ChannelListItemActionButtons |
the action buttons of a channel-list row | none | ChannelListItemActionButtons |
ChannelListItemUI |
a channel-list row | ChannelListItemUIProps |
ChannelListItemUI |
CommandChip |
the selected command in the composer | CommandChipProps |
CommandChip |
ContextMenu |
every context menu | ContextMenuProps |
ContextMenu |
ContextMenuContent |
a context menu's body and submenus | ContextMenuContentProps |
ContextMenuContent |
CooldownTimer |
the slow-mode countdown in the composer | none | CooldownTimer |
DateSeparator |
the date separators in message lists | DateSeparatorProps |
DateSeparator |
EditedMessagePreview |
the preview of the message being edited, above the composer's input | { message: LocalMessage; onCancel: () => void } |
built in |
EmojiPicker |
the emoji picker button in the composer | none | none: register EmojiPicker from stream-chat-react/emojis |
emojiSearchIndex |
the index the composer searches for emoji suggestions | EmojiSearchIndex, not a component |
none |
EmptyListIndicator |
what an empty channel list shows | none | an EmptyStateIndicator for channels |
EmptyStateIndicator |
what an empty message list shows | EmptyStateIndicatorProps |
EmptyStateIndicator |
EndReachedIndicator |
the start or the end of a channel list | { hasEnded: boolean; reached: "top" | "bottom" } |
renders nothing |
extractDisplayInfo |
how a user becomes the name and image an avatar shows | a function of { user }, not a component |
built in |
FileSizeIndicator |
a file size label | FileSizeIndicatorProps |
FileSizeIndicator |
FirstPageLoadingIndicator |
a channel list loading its first page | none | LoadingChannels |
Gallery |
the gallery's state and navigation | GalleryProps |
Gallery |
GalleryUI |
the gallery's slides and buttons | none | GalleryUI |
GiphyPreviewMessage |
a Giphy preview below the messages of VirtualizedMessageList, with separateGiphyPreview |
GiphyPreviewMessageProps |
GiphyPreviewMessage |
HeaderComponent |
content at the top of MessageList, or at its headerPosition |
none | none |
HeaderEndContent |
the end of the channel list's and the thread list's headers | none | none |
icons |
the SDK's icons, merged icon by icon | IconSlots, not a component |
the SDK's icons |
ImagePlaceholder |
an image that failed to load | ImagePlaceholderProps |
ImagePlaceholder |
LinkPreviewList |
the link previews in the composer | none | LinkPreviewList |
ListItem |
a row of a channel list | { item: unknown } |
a ChannelListItem for the channel |
LoadingIndicator |
the spinner of message lists, videos, the thread list and other parts | LoadingIndicatorProps |
LoadingIndicator |
LoadingNextPageIndicator |
a channel list loading its next page | { isLoading?: boolean } |
a LoadingIndicator while loading |
MessageActions |
a message's actions | none | MessageActions |
MessageAlsoSentInChannelIndicator |
the "also sent in channel" mark on a reply | none | MessageAlsoSentInChannelIndicator |
MessageBlocked |
a message blocked by moderation | none | MessageBlocked |
MessageBouncePrompt |
the dialog for a bounced message | MessageBouncePromptProps |
MessageBouncePrompt |
MessageComposerUI |
the composer UI that MessageComposer renders |
none; the default takes MessageComposerProps |
MessageComposerUI |
MessageDeleted |
a deleted message, in place of its bubble | MessageDeletedProps |
none |
MessageDeletedBubble |
the bubble of a deleted message | none | MessageDeletedBubble |
MessageEditedIndicator |
the "Edited" mark | none; the default takes MessageEditedIndicatorProps |
MessageEditedIndicator |
MessageListItem |
the li around each item of MessageList |
PropsWithChildren |
li |
MessageListMainPanel |
the panel around a message list and its notification list | PropsWithChildren |
built in |
MessageListWrapper |
the ul around the messages of MessageList |
PropsWithChildren |
ul |
MessageReactions |
a message's reactions | MessageReactionsProps |
MessageReactions |
MessageReactionsDetail |
the dialog that lists who reacted | MessageReactionsDetailProps |
MessageReactionsDetail |
MessageRepliesCountButton |
the replies button on a message | MessageRepliesCountButtonProps |
MessageRepliesCountButton |
MessageStatus |
sending, sent, delivered or read | none; the default takes MessageStatusProps |
MessageStatus |
MessageSystem |
a system message | EventComponentProps |
EventComponent |
MessageTimestamp |
a message's timestamp and, when set, the time in the gallery header for media shared in a message | MessageTimestampProps |
MessageTimestamp |
MessageTranslationIndicator |
the "translated" mark | TranslationIndicatorProps |
MessageTranslationIndicator |
MessageUI |
a whole message, in both lists unless VirtualMessage is set |
none: read useMessageContext() |
MessageUI |
Modal |
every modal | ModalProps |
GlobalModal |
ModalGallery |
a message's images, which open the gallery | ModalGalleryProps |
ModalGallery |
NewMessageNotification |
the "new messages" notification of a message list | NewMessageNotificationProps, without label |
NewMessageNotification |
Notification |
one notification in a NotificationList |
NotificationProps, with a ref |
Notification |
NotificationAnnouncer |
the screen-reader announcements of notifications, rendered by Chat |
none; the default takes NotificationAnnouncerProps |
NotificationAnnouncer |
NotificationList |
every notification list | NotificationListProps |
NotificationList |
PinIndicator |
the pinned mark | PinIndicatorProps |
PinIndicator |
PollActions |
a poll's buttons | none | PollActions |
PollContent |
a poll in a message | none | PollContent |
PollCreationDialog |
the dialog that creates a poll | PollCreationDialogProps |
PollCreationDialog |
PollHeader |
a poll's header | none | PollHeader |
PollOptionSelector |
one option of a poll | PollOptionSelectorProps |
PollOptionSelector |
ProgressIndicator |
the progress of an upload, in composer previews, message attachments and the voice recorder | ProgressIndicatorProps |
CircularProgressIndicator |
QuotedMessage |
the quoted message inside a message | none | QuotedMessage |
QuotedMessagePreview |
the quoted message above the composer's input | none; the default takes QuotedMessagePreviewProps |
QuotedMessagePreview |
reactionOptions |
the reactions users can pick | ReactionOptions, not a component |
defaultReactionOptions |
ReactionSelector |
the reaction picker | ReactionSelectorProps, with a ref |
ReactionSelector |
ReactionSelectorExtendedList |
the full reaction picker in the reactions dialog | the props of ReactionSelector.ExtendedList |
ReactionSelector.ExtendedList |
RecordingPermissionDeniedNotification |
the notice shown when microphone access is denied | RecordingPermissionDeniedNotificationProps |
RecordingPermissionDeniedNotification |
ReminderNotification |
a message's reminder mark | ReminderNotificationProps |
ReminderNotification |
Search |
the search UI | none | Search |
SearchBar |
the search input | none | SearchBar |
SearchResults |
the search results pane | none | SearchResults |
SearchResultsHeader |
the header of the results pane | none | SearchResultsHeader |
SearchResultsPresearch |
the results pane before the first search | SearchResultsPresearchProps |
SearchResultsPresearch |
SearchSourceResultList |
the result list of one search source | none; the default takes SearchSourceResultListProps |
SearchSourceResultList |
SearchSourceResultListFooter |
the end of one source's result list | none | SearchSourceResultListFooter |
SearchSourceResults |
the section of one search source | SearchSourceResultsProps: { searchSource } |
SearchSourceResults |
SearchSourceResultsEmpty |
a source with no results | none | SearchSourceResultsEmpty |
SearchSourceResultsHeader |
the header of one source's section | none | built in |
SearchSourceResultsLoadingIndicator |
a source loading results | none | SearchSourceResultsLoadingIndicator |
SendButton |
the send button | SendButtonProps |
SendButton |
SendToChannelCheckbox |
the "also send to channel" checkbox in a thread's composer | none | built in |
ShareLocationDialog |
the location-sharing dialog | ShareLocationDialogProps |
ShareLocationDialog |
StartRecordingAudioButton |
the button that starts a voice recording | StartRecordingAudioButtonProps |
StartRecordingAudioButton |
StopAIGenerationButton |
the stop button while an AI response is generated | ComponentProps<"button"> |
built in; null removes the button |
StreamedMessageText |
the text of an AI-generated message | StreamedMessageTextProps |
StreamedMessageText |
SummarizedMessagePreview |
the latest-message preview of a channel-list row | SummarizedMessagePreviewProps |
SummarizedMessagePreview |
TextareaComposer |
the composer's text input | none; the default takes TextareaComposerProps |
TextareaComposer |
ThreadHead |
the parent message above a thread's replies | MessageProps |
built in |
ThreadListEmptyPlaceholder |
an empty thread list | none | built in |
ThreadListItem |
a thread-list row | ThreadListItemProps |
ThreadListItem |
ThreadListItemUI |
the UI of a thread-list row | ThreadListItemUIProps |
ThreadListItemUI |
ThreadListLoadingIndicator |
a thread list loading | none | built in |
ThreadListUnseenThreadsBanner |
the banner about threads with new replies | none | built in |
ThreadStart |
the reply count under a thread's parent message | none | ThreadStart |
Timestamp |
the date inside timestamps and the edited mark | TimestampProps |
built in |
TypingIndicator |
the typing indicator of message lists | TypingIndicatorProps |
TypingIndicator in MessageList; none in VirtualizedMessageList |
UnreadMessagesNotification |
the floating unread notification | UnreadMessagesNotificationProps |
UnreadMessagesNotification |
UnreadMessagesSeparator |
the unread separator | UnreadMessagesSeparatorProps |
UnreadMessagesSeparator |
UploadedSizeIndicator |
the uploaded and total size of an upload | UploadedSizeIndicatorProps |
UploadedSizeIndicator |
VideoPlayer |
video playback | { controls?: boolean; isPlaying?: boolean; thumbnailUrl?: string; videoUrl?: string } |
built in |
VirtualMessage |
a whole message, in VirtualizedMessageList only |
none: read useMessageContext() |
none: MessageUI renders |
VoiceRecordingPreviewSlot |
the voice recording preview above the composer's attachments | none; the default takes VoiceRecordingPreviewSlotProps |
VoiceRecordingPreviewSlot |
The ComponentContextValue type also has FileDragAndDropContent and ScrollToLatestMessageButton. No component reads them, so setting them changes nothing.
Choosing between props and context
Use a key for a part the SDK renders, and a prop or your own children for a part you render yourself:
- Components you render, such as
ChannelHeader,ThreadHeader,MessageListandMessageComposer, have no key. Pass them props, or render something else in their place.ChannelHeader'sStartContent,EndContentandAvatarare props for this reason; see Header content. - Parts the SDK renders, such as a message, the date separators or the notification list, have a key. Set it with
WithComponentsaround the subtree that should use it. - Some components take a prop and read a key for the same part. The prop wins for that one instance.
Galleryrenders itsGalleryUIprop, else theGalleryUIkey, else the default.AttachmenttakesImage,Giphy,Fileand other props for the parts it renders, while theAttachmentkey replacesAttachmentitself.
Channel and Thread render only their children, so their content is yours to compose. See Composition.
Related hooks
useComponentContext()returns the overrides in effect, or{}outside every provider.useComponentContextIcons()returns every icon, with theiconsoverrides merged over the SDK's own. See Icons.