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

Message UI

The message UI is the component that draws one message. Message renders the MessageUI override with no props, and the UI reads the message and its handlers from MessageContext. The default is MessageUI. A message lists the parts it's made of, and A custom MessageUI explains how a replacement applies.

Best practices

  • Build a custom message UI from the exported parts, such as MessageText, MessageTimestamp and Attachment. They read the same context, and most need no props; Attachment takes the attachments to render.
  • Handle the kinds of message your app has. The default covers deleted, blocked, bounced and failed messages, polls and quoted messages. System messages go to the MessageSystem override instead.
  • Set VirtualMessage only when VirtualizedMessageList needs a different UI. There it wins over MessageUI.
  • Declare the overrides object once, outside the component, so every message doesn't re-render with each render of the parent.

Basic usage

The default MessageUI also takes any MessageContext value as a prop, and a prop wins over the context. A wrapper registered as the override changes one value for every message. This one shows the sender's avatar on every message, the user's own included:

import { MessageUI, WithComponents } from "stream-chat-react";

const MessageUIWithAllAvatars = () => <MessageUI showAvatar />;

const overrides = { MessageUI: MessageUIWithAllAvatars };

export const App = () => (
  <WithComponents overrides={overrides}>
    {/* the rest of the app */}
  </WithComponents>
);

UI customization

A message UI of your own composes the parts it needs. This one keeps the actions, a poll, a quoted message, the attachments, reactions, the replies button, the time and the delivery status:

import {
  Attachment,
  isMessageDeleted,
  MessageActions,
  MessageDeletedBubble,
  MessageReactions,
  MessageRepliesCountButton,
  MessageStatus,
  MessageText,
  MessageTimestamp,
  Poll,
  QuotedMessage,
  useChatContext,
  useMessageContext,
} from "stream-chat-react";

export const CustomMessageUI = () => {
  const { client } = useChatContext();
  const { handleAction, message } = useMessageContext();
  const poll = message.poll_id
    ? client.polls.fromState(message.poll_id)
    : undefined;

  if (isMessageDeleted(message)) return <MessageDeletedBubble />;

  return (
    <div className="custom-message">
      <MessageActions />
      {poll && <Poll poll={poll} />}
      {message.quoted_message && <QuotedMessage />}
      {message.attachments?.length ? (
        <Attachment
          actionHandler={handleAction}
          attachments={message.attachments}
        />
      ) : null}
      <MessageText />
      <MessageReactions />
      <MessageRepliesCountButton
        reply_count={message.reply_count}
        thread_participants={message.thread_participants}
      />
      <MessageTimestamp />
      <MessageStatus />
    </div>
  );
};

MessageRepliesCountButton renders nothing for a message without replies. When clicked, it calls its onClick prop if you pass one, and otherwise the message's handleOpenThread, which calls Message's openThread prop when set and opens the thread through workspace navigation otherwise. The parts and their props are on Message UI components. A shared location travels in message.shared_location, not in message.attachments; Attachments shows how to pass both.

Override paths

To change Set
Every message, in both lists and a thread's parent the MessageUI key
Every message in VirtualizedMessageList only the VirtualMessage key
One part of the default MessageUI that part's key, from the table in A message
A deleted message, replacing the whole UI the MessageDeleted key, which receives { message } and has no default
A system message the MessageSystem key
The parent message above a thread's replies the ThreadHead key, typed with the Message props and given only message
The items of the actions menu the messageActionSet prop of MessageActions, see Actions

Set the keys with WithComponents.

Props

The SDK renders the message UI with no props. The default MessageUI accepts Partial<MessageContextValue>, exported as MessageUIComponentProps, and merges it over the context. These are the values it reads:

Prop Type Default Description
endOfGroup boolean With groupedByUser, marks the last message of a group, the only one that shows the time and status.
firstOfGroup boolean Adds the str-chat__virtual-message__wrapper--first class.
groupedByUser boolean Adds the str-chat__virtual-message__wrapper--group class, and hides the time and status unless endOfGroup.
handleAction ActionHandlerReturnType from context Passed to Attachment for the attachments' actions.
highlighted boolean from context Adds the str-chat__message--highlighted class.
isMessageAIGenerated (message: LocalMessage) => boolean from context Renders the text of an AI-generated message with StreamedMessageText, and hides its edited mark.
isMyMessage () => boolean from context Adds the str-chat__message--me or str-chat__message--other class.
message LocalMessage from context The message to draw.
onUserClick ReactEventHandler from context Attached to the avatar.
onUserHover ReactEventHandler from context Attached to the avatar.
renderText RenderTextFunction from context Passed to MessageText or StreamedMessageText.
showAvatar boolean | 'incoming' | 'outgoing' 'incoming' Which messages get the avatar column: all (true), none (false), other users' or the user's own.

The default MessageUI re-renders when the message, its read and delivery state, its highlight or its grouping changes. Give the other props values that don't change, such as a renderText declared outside the component.

Hooks

  • useMessageContext() reads the message and its handlers.
  • useComponentContext() reads the override keys, for a part that renders another part.