VirtualizedMessageList

VirtualizedMessageList renders the messages of the channel or the thread it sits in, like MessageList, but keeps only the messages near the visible area in the DOM, with react-virtuoso. Message list UI compares the two lists.

Best practices

  • Use it for very long histories, or channels with a lot of traffic such as livestreams. Otherwise MessageList is simpler.
  • Remember what is off by default here and on in MessageList: date separators (withDateSeparator), grouping by sender (groupByUser) and the typing indicator, which renders only with a TypingIndicator override.
  • Pass react-virtuoso options through additionalVirtuosoProps.
  • Keep message UIs light. The list mounts and unmounts them as the user scrolls, and measures each one.
  • Don't rely on a message's DOM node: it exists only while the message is near the visible area.

Basic usage

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

export const LivestreamPanel = () => (
  <Channel channel={channel}>
    <ChannelHeader />
    <VirtualizedMessageList
      additionalVirtuosoProps={{ defaultItemHeight: 60 }}
      stickToBottomScrollBehavior="auto"
      withDateSeparator
    />
    <MessageComposer />
  </Channel>
);

defaultItemHeight gives the list a height to estimate with before it measures the messages. stickToBottomScrollBehavior="auto" follows new messages without a smooth scroll, which keeps up with a fast channel.

Scroll behavior

  • Opening. The list opens at the latest message, with its end in view. After a jump, it opens with the target message in the middle.
  • Following new messages. While the user is at the bottom, the list follows new messages with stickToBottomScrollBehavior. When the user sends a message, the list scrolls to it even if they were scrolled up. It doesn't follow while it shows an older window, or with suppressAutoscroll.
  • Loading pages. Reaching the top loads older messages. Reaching the bottom of an older interval loads newer ones.
  • Coming back. With scrollToLatestMessageOnFocus, the list scrolls to the latest message when the window regains focus after messages arrived in the background.

How the paginator holds intervals of messages is on Message paginators.

Notifications and unread UI

Like MessageList, the list renders the unread separator, UnreadMessagesNotification in a channel, NewMessageNotification, the scroll-to-latest button, and a NotificationList for its panel. Two parts differ:

  • The typing indicator renders below the messages only when you register a TypingIndicator override.
  • A Giphy preview renders with GiphyPreviewMessage below the messages when separateGiphyPreview is on.

Jumps on screen explains when the floating elements show.

Props

Prop Type Default Description
additionalVirtuosoProps VirtuosoProps from react-virtuoso Props for the underlying Virtuoso, applied over the list's own. react-virtuoso options such as defaultItemHeight, overscan and scrollSeekConfiguration go here. Its components are merged with the list's, and its scrollerRef and totalListHeightChanged are called after the list's own handlers. With useWindowScroll, the floating date separator takes its date from the first rendered message, so it can lag behind the messages on screen.
closeReactionSelectorOnClick boolean Closes the reaction selector once a reaction is picked.
customMessageRenderer (messageList: RenderedMessage[], index: number) => ReactElement Renders each item in place of the list's own renderer, which renders date separators, system messages, the unread separator and Message. It receives the processed list and the item's index. The list passes none of the props it drills to messages to the items your renderer draws; pass them to the Message you render.
getGroupStyles (params: GetGroupStylesParams) => GroupStyle getGroupStyles Decides each message's group style. It receives the list's groupByUser in its argument.
groupByUser boolean false Groups consecutive messages from the same sender.
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.
maxTimeBetweenGroupedMessages number no limit The longest gap, in milliseconds, between two messages of one group.
returnAllReadData boolean false Gives every own message its read and delivery data. By default only the last own message gets it.
reviewProcessedMessage ProcessMessagesParams["reviewProcessedMessage"] Called for each message as the list processes it. It returns the items to render in place of changes, the message with any separator added before it.
scrollToLatestMessageOnFocus boolean false Scrolls to the latest message when the window regains focus, if messages arrived while it was in the background.
separateGiphyPreview boolean false Renders a Giphy preview with GiphyPreviewMessage below the messages, instead of in the list.
showUnreadNotificationAlways boolean false Also shows the unread notification while the unread separator is below the visible area.
stickToBottomScrollBehavior "smooth" | "auto" "smooth" How the list follows new messages while at the bottom. "auto" jumps without animation, for channels with a lot of traffic.
suppressAutoscroll boolean false Stops the list from following new messages.
withDateSeparator boolean false 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.

These props also reach:

  • The parent message above the replies, in a thread.
  • The Giphy preview that separateGiphyPreview renders below the messages. GiphyPreviewMessage is typed with MessageProps and passes its props to Message.

To the parent message and the Giphy preview, the list passes only the props you set. A ThreadHead or GiphyPreviewMessage override that sets a prop and then spreads its props, such as <Message showAvatar {...props} />, keeps its own value.

With customMessageRenderer, the parent message and the Giphy preview still get these props.

The list renders each message with the VirtualMessage override if one is set, and otherwise with MessageUI. See ComponentContext.

Hook Returns
useVirtualizedMessageListContext() scrollToBottom, inside VirtualizedMessageList only. See VirtualizedMessageListContext
useMessagePaginator() the paginator the list renders: thread.messagePaginator inside a Thread, otherwise channel.messagePaginator
useChannel(), useThreadContext() the channel and the thread whose paginator the list renders
useStateStore(store, selector) a selection of the paginator's state
useMessageContext() the message and its handlers, inside each message the list renders