MessageList

MessageList renders every loaded message of the channel or the thread it sits in, and turns the user's scrolling into paginator calls. Inside a Thread it renders thread.messagePaginator, with the parent message above the replies; otherwise channel.messagePaginator. Message list UI explains what it shows, and how it differs from VirtualizedMessageList.

Best practices

  • Render it inside a Channel or a Thread. It picks the paginator from there, so the same <MessageList /> works in both.
  • Switch to VirtualizedMessageList when the DOM gets too large: very long histories, or channels with a lot of traffic.
  • Change how one message looks with the MessageUI override, and its actions with MessageActions and a messageActionSet. See Message actions. The list has no prop for actions.
  • Declare renderMessages, getGroupStyles and reviewProcessedMessage at module scope, or wrap them in useCallback.
  • Keep the li wrappers when you replace the rendering with renderMessages, so the markup stays a list.

Basic usage

import {
  Channel,
  ChannelHeader,
  MessageComposer,
  MessageList,
} from "stream-chat-react";

export const ChannelPanel = () => (
  <Channel channel={channel}>
    <ChannelHeader />
    <MessageList />
    <MessageComposer />
  </Channel>
);

In a thread, render it inside <Thread thread={thread}> the same way. The paginator's state, and how a component of yours picks the right one, are on Reading state in components.

Message grouping

MessageList groups consecutive messages from the same sender. Each message gets a group style, top, middle, bottom or single, and its li the matching class, such as str-chat__li--top. A new group starts when the sender changes, after a date separator, a system message or an edited message, and at a message with reactions. A message with attachments is always single.

  • groupByUser={false} turns grouping off: every message is single.
  • maxTimeBetweenGroupedMessages starts a new group when two messages are further apart than this many milliseconds. Without it, time doesn't split groups.
  • getGroupStyles replaces the rule. It receives one object with the message, the ones before and after it, groupByUser and maxTimeBetweenGroupedMessages, and returns the group style.

Custom message list rendering

renderMessages replaces how the processed list turns into elements. defaultRenderMessages is the SDK's own, so you can add to it:

import { defaultRenderMessages, MessageList } from "stream-chat-react";
import type { MessageRenderer } from "stream-chat-react";

const renderMessagesWithFooter: MessageRenderer = (options) => [
  ...defaultRenderMessages(options),
  <li className="str-chat__li" key="caught-up">
    You're all caught up!
  </li>,
];

export const ChannelMessages = () => (
  <MessageList renderMessages={renderMessagesWithFooter} />
);

Give every element a stable key. The options include the processed messages, with date separators and the header among them, the group styles, the read and delivery data, and the components in effect.

To change the items before they render, for example to drop or add one around a message, use reviewProcessedMessage instead.

Notifications and unread UI

Around the messages, <MessageList/> renders:

  • UnreadMessagesNotification, at the top of a channel's list while the unread separator is above the visible area or not loaded. Threads don't show it.
  • NewMessageNotification, while the user is scrolled up and new messages arrive, or the list shows an older interval.
  • ScrollToLatestMessageButton, while the list isn't at the latest message.
  • A NotificationList for the panel the list is in: channel or thread.

All four are override keys. MessageListMainPanel replaces the element around the list and its notification list. Jumps on screen explains when each one shows, and Notification lists and panels how notifications reach the list.

Props

Prop Type Default Description
getGroupStyles (params: GetGroupStylesParams) => GroupStyle getGroupStyles Decides each message's group style: "", "top", "middle", "bottom" or "single". The messages are RenderedMessage items.
groupByUser boolean true Groups consecutive messages from the same sender. false turns grouping off.
headerPosition TimestampNS the top of the list Where the HeaderComponent override renders: after the last message created before this time, in nanoseconds like message.created_at.
hideDeletedMessages boolean false Leaves deleted messages out of the list.
hideNewMessageSeparator boolean false Turns off the date separator added before the first message that arrived since the user last read the channel.
internalInfiniteScrollProps Partial<InfiniteScrollPaginatorProps>, with element as a tag name { element: "div", threshold: 250 } Props for the scroll container. threshold is how close to the top or the bottom, in pixels, the list loads the next page.
loadingMore boolean shows while an older page loads Whether the LoadingIndicator override shows above the messages while the paginator loads. Without the prop, it shows while the list loads an older page, which it starts only when the paginator has older messages and isn't already loading. true shows it during any load, such as a jump or a newer page; false never shows it.
maxTimeBetweenGroupedMessages number no limit The longest gap, in milliseconds, between two messages of one group.
renderMessages MessageRenderer defaultRenderMessages Turns the processed list into elements. See Custom message list rendering.
returnAllReadData boolean false Gives every own message its read and delivery data. By default only the last own message gets it, so only that one shows read status.
reviewProcessedMessage ProcessMessagesParams["reviewProcessedMessage"] Called for each message as the list processes it. It receives changes, the message with any separator added before it, and returns the items to render in their place.
scrolledUpThreshold number 200 How close to the bottom, in pixels, the list counts as at the bottom, and keeps following new messages.
showUnreadNotificationAlways boolean false Also shows the unread notification while the unread separator is below the visible area.
suppressAutoscroll boolean false Stops the list from scrolling to new messages.
withDateSeparator boolean true Adds date separators between days, and the floating date at the top while scrolling.

The list also takes these props and passes them to every <Message/> it renders: closeReactionSelectorOnClick, formatDate, onMentionsClick, onMentionsHover, onUserClick, onUserHover, reactionDetailsSort, renderText, showAvatar, sortReactions and unsafeHTML. They are documented on Message. unsafeHTML also reaches the MessageSystem override, by default <EventComponent/>. With it set, <EventComponent/> renders a system message's html when it has one, and its text otherwise.

In a thread, the parent message above the replies gets these props too. It doesn't get returnAllReadData.

Hook Returns
useMessageListContext() the processed messages, the scroll container and scrollToBottom, inside MessageList only. See MessageListContext
useMessagePaginator() the paginator the list renders
useChannel(), useThreadContext() the channel and the thread whose paginator the list renders
useStateStore(store, selector) a selection of the paginator's state, such as its items
useMessageContext() the message and its handlers, inside each message the list renders

useMessagePaginator() returns the paginator the list renders: thread.messagePaginator inside a Thread, otherwise channel.messagePaginator. Reading state in components shows it in use. The paginator's API is on Message paginator.