Message UI
The message view is the component that draws one message. Message renders the MessageItemView override with no props, and the view reads the message and its handlers from MessageContext. The default is MessageItemView, which lays out the avatar, the headers, the bubble, the reactions, the replies button and the footer, and renders the bubble's content with MessageContent. A message explains how the parts fit together.
Best practices
- Replace one part through its override key, and keep the rest of the default view. The keys are in Override paths.
- Add to the bubble with the content slots before you replace
MessageContentorMessageItemView. They render inside the bubble and need no props. - A message view of your own handles the kinds of message your app has. The default covers deleted, failed and error messages, Giphy previews, polls, quoted messages and AI-generated text. Blocked messages render
MessageBlocked, and system messagesMessageSystem, instead of the view. - Keep the long press menu in a view of your own: call
onLongPressfrom the context, and attachcontextMenuAnchorRefto the bubble. - Declare the
overridesobject once, outside the component that rendersWithComponents.
Basic usage
MessageItemView also takes its context values as props, and a prop wins over the context. A wrapper registered as the override changes one value for every message. This one turns off swipe to reply on the messages of bots:
import {
MessageItemView,
useMessageContext,
WithComponents,
} from "stream-chat-react-native";
const MessageItemViewWithoutBotSwipe = () => {
const { message } = useMessageContext();
return (
<MessageItemView
enableSwipeToReply={!message.user?.id?.startsWith("bot-")}
/>
);
};
const overrides = { MessageItemView: MessageItemViewWithoutBotSwipe };
export const MessagesWithoutBotSwipe = ({
children,
}: {
children: React.ReactNode;
}) => <WithComponents overrides={overrides}>{children}</WithComponents>;UI customization
A message view of your own composes the exported parts it needs. They read the same context, so most need no props. This one keeps the avatar, the bubble with its content, the reactions and the footer, and drops the headers, the replies button and swipe to reply:
import { StyleSheet, View } from "react-native";
import { convertTimestampToDate } from "stream-chat";
import {
MessageAuthor,
MessageContent,
MessageDeleted,
MessageFooter,
ReactionListBottom,
useMessageContext,
useTheme,
WithComponents,
} from "stream-chat-react-native";
const PlainMessageItemView = () => {
const { alignment, contextMenuAnchorRef, groupStyles, isMyMessage, message } =
useMessageContext();
const {
theme: { semantics },
} = useTheme();
const date = convertTimestampToDate(message.created_at);
if (message.type === "deleted") {
return (
<MessageDeleted
date={date}
groupStyle={`${alignment}_${groupStyles[0] ?? ""}`}
/>
);
}
return (
<View style={[styles.row, alignment === "right" && styles.rowReversed]}>
{alignment === "left" ? <MessageAuthor /> : null}
<View style={alignment === "right" ? styles.right : styles.left}>
<View ref={contextMenuAnchorRef}>
<MessageContent
backgroundColor={
isMyMessage ? semantics.chatBgOutgoing : semantics.chatBgIncoming
}
/>
</View>
<ReactionListBottom type="clustered" />
<MessageFooter date={date} />
</View>
</View>
);
};
const overrides = { MessageItemView: PlainMessageItemView };
export const PlainMessages = ({ children }: { children: React.ReactNode }) => (
<WithComponents overrides={overrides}>{children}</WithComponents>
);
const styles = StyleSheet.create({
left: { alignItems: "flex-start" },
right: { alignItems: "flex-end" },
row: { alignItems: "flex-end", flexDirection: "row", gap: 8 },
rowReversed: { flexDirection: "row-reverse" },
});MessageContent handles the presses, so the long press menu keeps working. The parts and their props are on Message UI components, and A custom MessageItemView shows another one.
Override paths
The default view renders these keys. Set them with WithComponents.
| Override key | Renders | Inside it |
|---|---|---|
MessageAuthor |
the sender's avatar, on messages aligned left | |
MessageHeader |
the marks above the bubble | MessagePinnedHeader, MessageReminderHeader, MessageSavedForLaterHeader, SentToChannelHeader |
ReactionListTop |
the reactions above the bubble, with reactionListPosition="top" |
ReactionListClustered, ReactionListItem, ReactionListCountItem |
MessageContent |
the bubble and its content | Reply, Gallery, FileAttachmentGroup, Attachment, StreamingMessageView, MessageLocation, MessageText (when set), the content slots |
MessageError |
the error badge on a failed or error message | |
MessageReplies |
the replies button, outside a thread | MessageRepliesAvatars |
ReactionListBottom |
the reactions under the bubble, with reactionListPosition="bottom" |
ReactionListClustered, ReactionListItem |
MessageFooter |
the sender's name, the status, the time and "Edited" | MessageStatus, MessageTimestamp |
MessageDeleted |
a deleted message, in place of all the above except MessageAuthor |
MessageFooter |
MessageSpacer |
last in the message row, outside the bubble | |
MessageSwipeContent |
the icon revealed while the user swipes the message |
FileAttachmentGroup renders each file through the Attachment key, and Attachment picks AudioAttachment, FileAttachment, Gallery, Giphy, UrlPreview, URLPreviewCompact or UnsupportedAttachment. The attachments are on Attachments, and the reactions on Reaction components. A poll renders Poll, which takes the PollContent key; see Poll components.
MessageItemView props
The SDK renders the view with no props. The default MessageItemView reads each of these from a context, and a prop wins over it.
| Prop | Type | Default | Description |
|---|---|---|---|
alignment |
Alignment |
from MessageContext |
'left' puts the avatar first and the bubble at the start; 'right' reverses the row. |
channel |
Channel |
from MessageContext |
The message's channel, passed to customMessageSwipeAction. |
contextMenuAnchorRef |
React.RefObject<ReactNativeElement | null> |
from MessageContext |
Attached to the view around MessageContent, which the message menu lines up with. |
customMessageSwipeAction |
({ channel, message, }: { channel: Channel; message: LocalMessage; }) => void |
from MessagesContext |
Called when the user swipes the message, in place of quoting it. |
enableMessageGroupingByUser |
boolean |
from MessagesContext |
Not used by the view. |
enableSwipeToReply |
boolean |
from MessagesContext |
Lets the user swipe the message to quote it. Deleted messages can't be swiped. |
groupStyles |
string[] |
from MessageContext |
The message's place in its group, passed to MessageDeleted. |
hasAttachmentActions |
boolean |
from MessageContext |
Whether an attachment has actions. With it, the view treats the message as having no quoted message when it picks the bubble's background and border. |
isMyMessage |
boolean |
from MessageContext |
Picks the outgoing or incoming bubble color. |
lastGroupMessage |
boolean |
from MessageContext |
The view re-renders when it changes. |
message |
LocalMessage |
from MessageContext |
The message, passed to customMessageSwipeAction and quoted on swipe. The parts read the message from MessageContext. |
messageSwipeToReplyHitSlop |
RectOrSize |
the screen's width on both sides | The hit slop of the swipe to reply gesture. |
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
The view re-renders when it changes. |
onlyEmojis |
boolean |
from MessageContext |
With it, and no quoted message, the bubble has no background and no border. |
otherAttachments |
Attachment[] |
from MessageContext |
A Giphy or Imgur attachment here, without a quoted message, gets a transparent bubble. |
reactionListPosition |
"bottom" | "top" |
from MessagesContext |
Renders ReactionListTop above the bubble, or ReactionListBottom under it. |
reactionListType |
"clustered" | "segmented" |
from MessagesContext |
Passed to the reaction list as type. |
setQuotedMessage |
(quotedMessage: LocalMessage | null) => void |
from MessageContext |
Quotes the message when the user swipes it. |
thread |
Thread |
from ThreadContext |
Inside a thread, the replies button doesn't render. |
MessageContent props
MessageContent renders the bubble: a Pressable that reports presses with the messageContent emitter, the bubble's background and corners, the content slots, and the parts in messageContentOrder. A quoted message renders Reply, and pressing it calls goToMessage. The text of an AI-generated message renders StreamingMessageView in place of the text. A location renders the MessageLocation key, which has no default.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for the bubble's Pressable, applied after its handlers. onPressOut is called after the list re-enables scrolling. |
alignment |
Alignment |
from MessageContext |
Puts the bubble's tail on the right or the left corner. |
backgroundColor |
ColorValue |
The bubble's color. MessageItemView passes the outgoing or incoming color, or transparent. |
|
enableMessageGroupingByUser |
boolean |
from MessagesContext |
With it, the newest message uses the lastMessageContainer style. |
goToMessage |
(messageId: string) => void |
from MessageContext |
Called with the quoted message's ID when the user presses it. Without it, the quoted message can't be pressed. |
groupStyles |
string[] |
from MessageContext |
A bottom or single message gets a tail corner. |
hasInteractiveAccessibilityContent |
boolean |
from MessageContext |
Lets screen readers reach the parts one by one instead of the bubble as a whole. |
hidePaddingBottom |
boolean |
true when the bubble holds a poll, a Giphy or Imgur attachment without a quoted message, or nothing but one image or video, one file, or text, or when it ends with text |
Removes the bubble's bottom padding. |
hidePaddingHorizontal |
boolean |
true when the bubble holds a poll, a Giphy or Imgur attachment without a quoted message, or nothing but one image or video or one file |
Removes the bubble's side padding. |
hidePaddingTop |
boolean |
true when the bubble holds a poll, a Giphy or Imgur attachment without a quoted message, or nothing but one image or video, one file, or text |
Removes the bubble's top padding. |
isAttachmentEqual |
(prevAttachment: Attachment, nextAttachment: Attachment) => boolean |
from MessagesContext |
An extra check when the bubble re-renders. |
isMyMessage |
boolean |
from MessageContext |
Picks the accessibility label: "from you" or "from" the sender. |
isSingleFile |
boolean |
Not used. | |
isVeryLastMessage |
boolean |
Passed by MessageItemView. With grouping on, the newest message uses the lastMessageContainer style. |
|
message |
LocalMessage |
from MessageContext |
The message. |
messageContentOrder |
MessageContentType[] |
from MessageContext |
The parts to render, in order. |
messageGroupedSingleOrBottom |
boolean |
false |
Passed by MessageItemView. Picks the messageGroupedSingleOrBottomContainer style over messageGroupedTopContainer. |
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
The bubble re-renders when it changes. |
noBorder |
boolean |
Removes the bubble's border. | |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press on the bubble or the quoted message. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press on the bubble or the quoted message. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press on the bubble or the quoted message starts. |
otherAttachments |
Attachment[] |
from MessageContext |
The attachments of the attachments part. When the first has actions, the text isn't shown. |
preventPress |
boolean |
from MessageContext |
Turns off presses on the bubble and the quoted message. |
t |
StreamTFunction |
from TranslationContext |
The bubble re-renders when it changes. |
Content slots
These override keys have no default. Each renders inside the bubble when set, and receives no props: read what you need from useMessageContext().
| Override key | Where it renders |
|---|---|
MessageContentTopView |
above the content |
MessageContentBottomView |
under the content |
MessageContentLeadingView |
before the content, in a row with it |
MessageContentTrailingView |
after the content, in a row with it |
MessageSpacer renders last in the message row, beside the content and outside the bubble, and receives no props either. MessageSwipeContent takes no props: it's the round reply icon that appears beside the message while the user swipes it.
Related hooks
useMessageContext()reads the message and its handlers.useMessagesContext()reads the settings ofChannelorThread, such asreactionListPosition.useComponentsContext()reads the override keys, for a part that renders another part.useTheme()reads the theme. The view's styles are undertheme.messageItemView.