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
Channelor aThread. It picks the paginator from there, so the same<MessageList />works in both. - Switch to
VirtualizedMessageListwhen the DOM gets too large: very long histories, or channels with a lot of traffic. - Change how one message looks with the
MessageUIoverride, and its actions withMessageActionsand amessageActionSet. See Message actions. The list has no prop for actions. - Declare
renderMessages,groupStylesandreviewProcessedMessageat module scope, or wrap them inuseCallback. - Keep the
liwrappers when you replace the rendering withrenderMessages, 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.
noGroupByUserturns grouping off: every message issingle.maxTimeBetweenGroupedMessagesstarts a new group when two messages are further apart than this many milliseconds. Without it, time doesn't split groups.groupStylesreplaces 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
NotificationListfor the panel the list is in:channelorthread.
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.
Related hooks
| 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.