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";
// The list finds date separators by the class and reads their dates from `data-date`.
const DayOnlySeparator = ({ date, floating }: DateSeparatorProps) => (
<div
className={`str-chat__date-separator${floating ? " str-chat__date-separator--floating" : ""}`}
data-date={date.toISOString()}
>
{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. Apart from MessageUI and VirtualMessage, those keys are typed React.ComponentType without props, so an override whose props are required doesn't compile. Where the default takes props of its own, the SDK still passes none: an override that renders the default can pass them, as in MessageStatus: () => <MessageStatus MessageReadStatus={ReadIcon} />. 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: Actions | none | AdditionalMessageComposerActions |
Attachment |
a message's attachments: Parts of the default message UI | AttachmentProps |
Attachment |
AttachmentFileIcon |
the file-type icon on file attachments | FileIconProps |
FileIcon |
AttachmentPreviewList |
the attachment previews in the composer: The preview set | none; the default takes AttachmentPreviewListProps |
AttachmentPreviewList |
AttachmentSelector |
the composer's attachment button and its menu: Actions | none | AttachmentSelector, and SimpleAttachmentSelector while editing a message |
AttachmentSelectorInitiationButtonContents |
the content of the attachment button: Message composer UI | none | the plus icon |
AttachmentUploadProgressIndicator |
the progress of a message attachment that is still uploading | AttachmentUploadProgressIndicatorProps |
AttachmentUploadProgressIndicator |
AudioRecorder |
the voice recorder in the composer: Actions | none | AudioRecorder |
AutocompleteSuggestionItem |
one item of the composer's suggestion list: Message composer UI | SuggestionItemProps |
SuggestionListItem |
AutocompleteSuggestionList |
the composer's suggestion list: Message composer UI | SuggestionListProps |
SuggestionList |
Avatar |
a user's avatar. The tiles of <GroupAvatar/> keep the default Avatar: see 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. BaseImage lists the images it reaches | BaseImageProps |
BaseImage |
CalloutDialog |
the content of a callout dialog | CalloutDialogProps |
built in |
ChannelListHeader |
the channel list's header: Header | none; the default takes ChannelListHeaderProps |
ChannelListHeader |
ChannelListItemActionButtons |
the action buttons of a channel-list row: Channel preview actions | none | ChannelListItemActionButtons |
ChannelListItemUI |
a channel-list row: Channel previews | ChannelListItemUIProps |
ChannelListItemUI |
CommandChip |
the selected command in the composer: The input | CommandChipProps |
CommandChip |
ContextMenu |
every context menu: Ready-made dialogs | ContextMenuProps |
ContextMenu |
ContextMenuContent |
a context menu's body and submenus: Ready-made dialogs | ContextMenuContentProps |
ContextMenuContent |
CooldownTimer |
the slow-mode countdown in the composer: Actions | none | CooldownTimer |
DateSeparator |
the date separators in message lists: Separators | DateSeparatorProps |
DateSeparator |
EditedMessagePreview |
the preview of the message being edited, above the composer's input: The preview set | EditedMessagePreviewProps |
EditedMessagePreview |
EmojiPicker |
the emoji picker button in the composer: Actions | none | none: register EmojiPicker from stream-chat-react/emojis |
EmptyListIndicator |
what an empty channel list shows: List states UI | none | an EmptyStateIndicator for channels |
EmptyStateIndicator |
what an empty message list shows: Message list UI | EmptyStateIndicatorProps |
EmptyStateIndicator |
EndReachedIndicator |
the start or the end of a channel list: List states UI | { 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 |
FileDragAndDropContent |
the overlay WithDragAndDropUpload shows while files are dragged over it: WithDragAndDropUpload |
FileDragAndDropContentProps: { isDragRejected } |
FileDragAndDropContent |
FileSizeIndicator |
a file size label | FileSizeIndicatorProps |
FileSizeIndicator |
FirstPageLoadingIndicator |
a channel list or the thread list loading its first page: FirstPageLoadingIndicator and ListLoadErrorIndicator | 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, which is MessageProps |
GiphyPreviewMessage |
HeaderComponent |
content at the top of MessageList, or at its headerPosition |
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: The preview set | none | LinkPreviewList |
ListItem |
a row of a channel list: Channel list UI | { item: unknown } |
a ChannelListItem for the channel |
ListLoadErrorIndicator |
a channel list or the thread list whose first page failed to load: FirstPageLoadingIndicator and ListLoadErrorIndicator | ListLoadErrorIndicatorProps |
ListLoadErrorIndicator, with the list's text |
LoadingIndicator |
the spinner of message lists, videos, the thread list and other parts: LoadingIndicator lists them | LoadingIndicatorProps |
LoadingIndicator |
LoadingNextPageIndicator |
a channel list loading its next page: List states UI | { isLoading?: boolean } |
a LoadingIndicator while loading |
MessageActions |
a message's actions: Message actions | none | MessageActions |
MessageAlsoSentInChannelIndicator |
the "also sent in channel" mark on a reply: Parts of the default message UI | none | MessageAlsoSentInChannelIndicator |
MessageBlocked |
a message blocked by moderation: Parts of the default message UI | none | MessageBlocked |
MessageBouncePrompt |
the dialog for a bounced message | MessageBouncePromptProps |
MessageBouncePrompt |
MessageBubbleContent |
what a message's bubble holds: Bubble content | none; the default takes MessageBubbleContentProps |
MessageBubbleContent |
MessageComposerPreviews |
the previews above the composer's input: Previews | none; the default takes MessageComposerPreviewsProps |
MessageComposerPreviews |
MessageComposerUI |
the composer UI that MessageComposer renders: Message composer UI |
none; the default takes MessageComposerProps |
MessageComposerUI |
MessageDeleted |
a deleted message, in place of its bubble: Parts of the default message UI | MessageDeletedProps |
none |
MessageDeletedBubble |
the bubble of a deleted message: Parts of the default message UI | none | MessageDeletedBubble |
MessageEditedIndicator |
the "Edited" mark: Parts of the default message UI | none; the default takes MessageEditedIndicatorProps |
MessageEditedIndicator |
MessageListItem |
the li around each item of MessageList: Message list UI |
MessageListItemProps: className, data-index, data-message-id and data-testid, to spread onto the element |
li |
MessageListMainPanel |
the panel around a message list and its notification list | PropsWithChildren |
built in |
MessageListWrapper |
the ul around the messages of MessageList: Message list UI |
MessageListWrapperProps: className |
ul |
MessageReactions |
a message's reactions: Parts of the default message UI | MessageReactionsProps |
MessageReactions |
MessageReactionsDetail |
the dialog that lists who reacted | MessageReactionsDetailProps |
MessageReactionsDetail |
MessageRepliesCountButton |
the replies button on a message: Parts of the default message UI | MessageRepliesCountButtonProps |
MessageRepliesCountButton |
MessageStatus |
sending, sent, delivered or read: Parts of the default message UI | none; the default takes MessageStatusProps |
MessageStatus |
MessageSystem |
a system message: Parts of the default message UI | EventComponentProps |
EventComponent |
MessageText |
a message's text, also the text of an AI-generated message while StreamedMessageText streams it: Bubble content |
MessageTextProps |
MessageText |
MessageTimestamp |
a message's timestamp and, when set, the time in the gallery header for media shared in a message: Parts of the default message UI | MessageTimestampProps |
MessageTimestamp |
MessageTranslationIndicator |
the "translated" mark: Parts of the default message UI | TranslationIndicatorProps |
MessageTranslationIndicator |
MessageUI |
a whole message, in both lists unless VirtualMessage is set: How a message is rendered |
none: read useMessageContext() |
MessageUI |
Modal |
every modal: Modals | ModalProps |
GlobalModal |
ModalGallery |
a message's images, which open the gallery | ModalGalleryProps |
ModalGallery |
NewMessageNotification |
the "new messages" notification of a message list: Floating notifications | NewMessageNotificationProps, without label |
NewMessageNotification |
Notification |
one notification in a NotificationList: Replacing the list or a notification |
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: Notification lists and panels | NotificationListProps |
NotificationList |
PinIndicator |
the pinned mark: Parts of the default message UI | PinIndicatorProps |
PinIndicator |
PollActions |
a poll's buttons | none | PollActions |
PollContent |
a poll in a message | none | PollContent |
PollCreationDialog |
the dialog that creates a poll: Message composer UI | PollCreationDialogProps |
PollCreationDialog |
PollCreationDialogControls |
the buttons at the bottom of the poll creation dialog: Message composer UI | PollCreationDialogControlsProps |
PollCreationDialogControls |
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: Bubble content | none | QuotedMessage |
QuotedMessagePreview |
the quoted message above the composer's input: The preview set | none; the default takes QuotedMessagePreviewProps |
QuotedMessagePreview |
reactionOptions |
the reactions users can pick | ReactionOptions, not a component |
defaultReactionOptions |
ReactionSelector |
the reaction picker | ReactionSelectorProps |
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: Parts of the default message UI | ReminderNotificationProps |
ReminderNotification |
ScrollToLatestMessageButton |
the button of a message list that scrolls to the latest message: Floating notifications | ScrollToLatestMessageButtonProps |
ScrollToLatestMessageButton |
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: Actions | SendButtonProps |
SendButton |
SendToChannelCheckbox |
the "also send to channel" checkbox in a thread's composer: In threads | none | built in |
ShareLocationDialog |
the location-sharing dialog: Message composer UI | ShareLocationDialogProps |
ShareLocationDialog |
StartRecordingAudioButton |
the button that starts a voice recording: Actions | StartRecordingAudioButtonProps |
StartRecordingAudioButton |
StopAIGenerationButton |
the stop button while an AI is thinking or generating a reply: Actions | ComponentProps<"button"> |
built in; null removes the button |
StreamedMessageText |
the text of an AI-generated message: Bubble content | StreamedMessageTextProps |
StreamedMessageText |
SummarizedMessagePreview |
the latest-message preview of a channel-list row and of a thread-list row: Channel list UI | SummarizedMessagePreviewProps |
SummarizedMessagePreview |
TextareaComposer |
the composer's text input: The input | none; the default takes TextareaComposerProps |
TextareaComposer |
ThreadHead |
the parent message above a thread's replies: Thread UI | MessageProps |
built in |
ThreadListEmptyPlaceholder |
an empty thread list: List states UI | none | built in |
ThreadListHeader |
the thread list's header: Header | none; the default takes ThreadListHeaderProps |
ThreadListHeader |
ThreadListItem |
a thread-list row: Thread list items | ThreadListItemProps |
ThreadListItem |
ThreadListItemUI |
the UI of a thread-list row: Thread list items | ThreadListItemUIProps |
ThreadListItemUI |
ThreadListLoadingIndicator |
a thread list loading its next page: List states UI | none | built in |
ThreadListUnseenThreadsBanner |
the banner about threads with new replies: The unseen threads banner | none | built in |
ThreadStart |
the reply count under a thread's parent message: Thread UI | none | ThreadStart |
Timestamp |
the date inside timestamps and the edited mark | TimestampProps |
built in |
TypingIndicator |
the typing indicator of message lists: Typing indicator | TypingIndicatorProps |
TypingIndicator in MessageList; none in VirtualizedMessageList |
UnreadMessagesNotification |
the floating unread notification: Floating notifications | UnreadMessagesNotificationProps |
UnreadMessagesNotification |
UnreadMessagesSeparator |
the unread separator: Separators | 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: Message list UI |
none: read useMessageContext() |
none: MessageUI renders |
VoiceRecordingPreviewSlot |
the voice recording preview above the composer's attachments: The preview set | none; the default takes VoiceRecordingPreviewSlotProps |
VoiceRecordingPreviewSlot |
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,EndContent,Title,SubtitleandAvatarare props for this reason. The header in Channel UI shows which part each one replaces. - 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.