Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

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 MessageContent or MessageItemView. 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 messages MessageSystem, instead of the view.
  • Keep the long press menu in a view of your own: call onLongPress from the context, and attach contextMenuAnchorRef to the bubble.
  • Declare the overrides object once, outside the component that renders WithComponents.

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:

src/MessageItemViewWithoutBotSwipe.tsx
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:

src/PlainMessageItemView.tsx
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.

  • useMessageContext() reads the message and its handlers.
  • useMessagesContext() reads the settings of Channel or Thread, such as reactionListPosition.
  • useComponentsContext() reads the override keys, for a part that renders another part.
  • useTheme() reads the theme. The view's styles are under theme.messageItemView.