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

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, groupStyles 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.

  • noGroupByUser 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.
  • groupStyles replaces the rule. It receives each message with the ones before and after it, 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.
  • The scroll-to-latest button, while the list isn't at the latest message.
  • A NotificationList for the panel the list is in: channel or thread.

The first two and the notification list are override keys. The scroll-to-latest button has none. 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
groupStyles (message, previousMessage, nextMessage, noGroupByUser, maxTimeBetweenGroupedMessages?) => GroupStyle getGroupStyles Decides each message's group style: "", "top", "middle", "bottom" or "single". The messages are RenderedMessage items.
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.
noGroupByUser boolean false Turns grouping off.
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: additionalMessageComposerProps, closeReactionSelectorOnClick, formatDate, onMentionsClick, onMentionsHover, onUserClick, onUserHover, reactionDetailsSort, renderText, sortReactions and unsafeHTML. They are documented on Message. unsafeHTML also renders system messages as HTML.

The list also passes disableQuotedMessages and showAvatar to each Message, but neither has an effect: Message doesn't hand showAvatar to the message UI, and the quote action doesn't check disableQuotedMessages.

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.