MessageList

The MessageList component renders a scrollable list of messages. The UI for each individual message is rendered conditionally based on its message.type value. The list renders date separators, message reactions, new message notifications, system messages, deleted messages, and standard messages containing text and/or attachments.

By default, the MessageList loads the most recent 25 messages held in the channel.state. More messages are fetched from the Stream Chat API and loaded into the DOM on scrolling up the list. The currently loaded messages are held in the ChannelStateContext and can be referenced with our custom hook.

const { messages } = useChannelStateContext();

The MessageList has no required props and by default pulls overridable data from the various contexts established in the Channel component. Customization of the messages rendered within the list is handled by the Message UI component.

Basic Usage

As a context consumer, the MessageList component must be rendered as a child of the Channel component. It can be rendered with or without a provided messages prop. Providing your own messages value will override the default value drawn from the ChannelStateContext.

Example 1 - without messages prop

<Chat client={client}>
  <ChannelList />
  <Channel>
    <MessageList />
    <MessageInput />
  </Channel>
</Chat>

Example 2 - with messages prop

const customMessages = [
  // array of messages
];

<Chat client={client}>
  <ChannelList />
  <Channel>
    <MessageList messages={customMessages} />
    <MessageInput />
  </Channel>
</Chat>;

Message grouping

The MessageList internally creates a mapping of message id to a style group. There are 4 style groups - 'middle' | 'top' | 'bottom' | 'single'. Later these group style tags are incorporated into the class of the <li/> element that wraps the Message component. This allows us to style messages by their position in the message group. An example of such class is str-chat__li str-chat__li--bottom.

Custom message list rendering

You can completely change the way the message list is rendered by providing a custom renderMessages function. This function takes all the messages fetched so far (along with some additional data) and returns an array of React elements to render. By overriding the default behavior, you can add custom elements to the message list, change the way messages are grouped, add custom separators between messages, etc.

If you provide a custom renderMessages function, it’s your responsibility to render each message type correctly. You can use the default implementation as a reference. Or, if you just want to tweak a few things here and there, you can call defaultRenderMessages from your custom renderMessages function and build from there.

In this example, we use the default implementation for rendering a message list, and we add a custom element at the bottom of the list:

const customRenderMessages: MessageRenderer<StreamChatGenerics> = (options) => {
  const elements = defaultRenderMessages(options);

  elements.push(<li key="caught-up">You're all caught up!</li>);

  return elements;
};

const CustomMessageList = () => (
  <MessageList renderMessages={customRenderMessages} />
);

Make sure that the elements you return have key, as they will be rendered as an array. It’s also a good idea to wrap each element with <li> to keep your markup semantically correct.

MessageList will re-render every time renderMessages function changes. For best performance, make sure that you don’t recreate renderMessages function on every render: either move it to the global or module scope, or wrap it with useCallback.

Custom message list rendering is only supported in MessageList and is currently not supported in VirtualizedMessageList.

Props

additionalMessageInputProps

Additional props to be passed to the MessageInput component, available props. It is rendered when editing a message.

Type
object

closeReactionSelectorOnClick

If true, picking a reaction from the ReactionSelector component will close the selector.

TypeDefault
booleanfalse

customMessageActions

An object containing custom message actions (key) and function handlers (value). For each custom action a dedicated item (button) in MessageActionsBox is rendered. The key is used as a display text inside the button. Therefore, it should not be cryptic but rather bear the end user in mind when formulating it.

const customActions = {
  "Copy text": (message) => {
    navigator.clipboard.writeText(message.text || "");
  },
};

<MessageList customMessageActions={customActions} />;

Custom action item “Copy text” in the message actions box:

Image of a custom action item

Type
object

disableDateSeparator

If true, disables the injection of date separator UI components in the Channel MessageList component.

TypeDefault
booleanfalse

disableQuotedMessages

If true, disables the ability for users to quote messages.

TypeDefault
booleanfalse

formatDate

Overrides the default date formatting logic, has access to the original date object.

Type
(date: Date) => string

getDeleteMessageErrorNotification

Function that returns the notification text to be displayed when the delete message request fails. This function receives the deleted message object as its argument.

Type
(message: StreamMessage) => string

getFetchReactionsErrorNotification

Function that returns the notification text to be displayed when loading message reactions fails. This function receives the current message object as its argument.

Type
(message: StreamMessage) => string

getFlagMessageErrorNotification

Function that returns the notification text to be displayed when a flag message request fails. This function receives the flagged message object as its argument.

Type
(message: StreamMessage) => string

getFlagMessageSuccessNotification

Function that returns the notification text to be displayed when a flag message request succeeds. This function receives the flagged message object as its argument.

Type
(message: StreamMessage) => string

getMarkMessageUnreadErrorNotification

Function that returns the notification text to be displayed when a mark message unread request fails. This function receives the marked message object as its argument.

Type
(message: StreamMessage) => string

getMarkMessageUnreadSuccessNotification

Function that returns the notification text to be displayed when a mark message unread request succeeds. This function receives the marked message object as its argument.

Type
(message: StreamMessage) => string

getMuteUserErrorNotification

Function that returns the notification text to be displayed when a mute user request fails. This function receives the muted user object as its argument.

Type
(user: UserResponse) => string

getMuteUserSuccessNotification

Function that returns the notification text to be displayed when a mute user request succeeds. This function receives the muted user object as its argument.

Type
(user: UserResponse) => string

getPinMessageErrorNotification

Function that returns the notification text to be displayed when a pin message request fails. This function receives the pinned message object as its argument.

Type
(message: StreamMessage) => string

groupStyles

Callback function to map each message in the list to a group style ('middle' | 'top' | 'bottom' | 'single').

Type
(message: StreamMessage, previousMessage: StreamMessage, nextMessage: StreamMessage, noGroupByUser: boolean, maxTimeBetweenGroupedMessages?: number) => GroupStyle

hasMore

Whether the list has more items to load.

headerPosition

Position to render the HeaderComponent in the list.

Type
number

hideDeletedMessages

If true, removes the MessageDeleted components from the list.

TypeDefault
booleanfalse

hideNewMessageSeparator

If true, hides the DateSeparator component that renders when new messages are received in a channel that’s watched but not active.

TypeDefault
booleanfalse

internalInfiniteScrollProps

Additional props for the underlying InfiniteScroll component.

Type
object

loadingMore

Whether the list is currently loading more items.

loadMore

Function called when more messages are to be loaded, provide your own function to override the handler stored in context.

maxTimeBetweenGroupedMessages

Maximum time in milliseconds that should occur between messages to still consider them grouped together.

Type
number

Message

Custom UI component to display an individual message.

TypeDefault
componentMessageSimple

messageActions

Array of allowed message actions (ex: ‘edit’, ‘delete’, ‘reply’). To disable all actions, provide an empty array.

TypeDefault
array[‘edit’, ‘delete’, ‘flag’, ‘mute’, ‘pin’, ‘quote’, ‘react’, ‘reply’]

messageLimit

The limit to use when paginating new messages (the page size).

After mounting, the MessageList component checks if the list is completely filled with messages. If there is some space left in the list, MessageList will load the next page of messages, but it will do so only once. This means that if your messageLimit is too low, or if your viewport is very large, the list will not be completely filled. Set the limit with this in mind.

TypeDefault
number100

messages

The messages to render in the list. Provide your own array to override the data stored in context.

noGroupByUser

If true, turns off message UI grouping by user.

TypeDefault
booleanfalse

onlySenderCanEdit

If true, only the sender of the message has editing privileges. If false also channel capability update-any-message has to be enabled in order a user can edit other users’ messages.

TypeDefault
booleanfalse

onMentionsClick

Custom action handler function to run on click on a @mention in a message.

onMentionsHover

Custom action handler function to run on hover over a @mention in a message.

onUserClick

Custom action handler function to run on click of user avatar.

Type
(event: React.BaseSyntheticEvent, user: User) => void

onUserHover

Custom action handler function to run on hover of user avatar.

Type
(event: React.BaseSyntheticEvent, user: User) => void

openThread

Custom handler invoked when the button in the Message component that opens Thread component is clicked. To be able to define custom logic to openThread, we need to have a wrapper around MessageList component and reach out to ChannelActionContext for the default openThread function.

import { useCallback } from "react";
import { MessageList, useChannelActionContext } from "stream-chat-react";
import type { StreamMessage } from "stream-chat-react";
import type { StreamChatGenerics } from "./types";

const MessageListWrapper = () => {
  const { openThread: contextOpenThread } =
    useChannelActionContext<StreamChatGenerics>();

  const openThread = useCallback(
    (
      message: StreamMessage<StreamChatGenerics>,
      event?: React.BaseSyntheticEvent,
    ) => {
      // custom logic
      contextOpenThread(message, event);
    },
    [contextOpenThread],
  );

  return <MessageList openThread={openThread} />;
};
TypeDefault
(message: StreamMessage, event?: React.BaseSyntheticEvent) => voidChannelActionContextValue[‘openThread’]

pinPermissions

The user roles allowed to pin messages in various channel types (deprecated in favor of channelCapabilities).

TypeDefault
objectdefaultPinPermissions

renderText

Custom function to render message text content.

TypeDefault
functionrenderText

renderMessages

Custom function to render message text content.

TypeDefault
functiondefaultRenderMessages

Parameters

The function receives a single object with the following properties:

NameTypeDescription
componentsComponentContextValueUI components, including possible overrides
customClassesobjectObject containing custom CSS classnames to override the library’s default container CSS
lastReceivedMessageIdstringThe latest message ID in the current channel
messageGroupStylesstring[]An array of potential styles to apply to a grouped message (ex: top, bottom, single)
messagesArray<ChannelStateContextValue[‘messages’]>The messages to render in the list
readDataobjectThe read state for for messages submitted by the user themselves
sharedMessagePropsobjectObject containing props that can be directly passed to the Message component

Return value

The function is expected to return an array of valid React nodes: Array<ReactNode>. For best performance, each node should have a key.

retrySendMessage

Custom action handler to retry sending a message after a failed request.

returnAllReadData

If true, readBy data supplied to the Message components will include all user read states per sent message. By default, only readBy data for a user’s most recently sent message is returned.

TypeDefault
booleanfalse

reviewProcessedMessage

Allows to review changes introduced to messages array on per message basis (for example date separator injection before a message). The array returned from the function is appended to the array of messages that are later rendered into React elements in the MessageList.

The function expects a single parameter, which is an object containing the following attributes:

  • changes - array of messages representing the changes applied around a given processed message
  • context - configuration params and information forwarded from processMessages
  • index - index of the processed message in the original messages array
  • messages - array of messages retrieved from the back-end
  • processedMessages - newly built array of messages to be later rendered

The context has the following parameters:

  • userId - the connected user ID;
  • enableDateSeparator - flag determining whether the date separators will be injected Enable date separator
  • hideDeletedMessages - flag determining whether deleted messages would be filtered out during the processing
  • hideNewMessageSeparator - disables date separator display for unread incoming messages
  • lastRead: Date when the channel has been last read. Sets the threshold after everything is considered unread

The example below demonstrates how the custom logic can decide, whether deleted messages should be rendered on a given date. In this example, the deleted messages neither the date separator would be rendered if all the messages on a given date are deleted.

const getMsgDate = (msg) =>
  (msg &&
    msg.created_at &&
    isDate(msg.created_at) &&
    msg.created_at.toDateString()) ||
  "";

const dateSeparatorFilter = (msg) => msg.customType !== "message.date";

const msgIsDeleted = (msg) => msg.type === "deleted";

const reviewProcessedMessage = ({
  changes,
  context,
  index,
  messages,
  processedMessages,
}) => {
  if (!context.enableDateSeparator) return changes;

  const changesWithoutSeparator = changes.filter(dateSeparatorFilter);
  const dateSeparatorInjected =
    changesWithoutSeparator.length !== changes.length;
  const previousProcessedMessage =
    processedMessages[processedMessages.length - 1];
  const processedMessage = messages[index];
  const processedMessageDate = getMsgDate(processedMessage);

  if (dateSeparatorInjected) {
    if (!processedMessageDate) return changes;
    const followingMessages = messages.slice(index + 1);
    let allFollowingMessagesOnDateDeleted = false;

    for (const followingMsg of followingMessages) {
      const followingMsgDate = getMsgDate(followingMsg);
      if (followingMsgDate !== processedMessageDate) break;
      allFollowingMessagesOnDateDeleted = followingMsg.type === "deleted";
    }

    return allFollowingMessagesOnDateDeleted ? [] : changes;
  } else if (
    msgIsDeleted(processedMessage) &&
    getMsgDate(previousProcessedMessage) !== getMsgDate(processedMessage)
  ) {
    return [];
  } else {
    return changes;
  }
};

scrolledUpThreshold

The pixel threshold to determine whether the user is scrolled up in the list. When scrolled up in the active channel, the MessageNotification component displays when new messages arrive.

TypeDefault
number200

showUnreadNotificationAlways

The floating notification informing about unread messages will be shown when the UnreadMessagesSeparator is not visible. The default is false, that means the notification is shown only when viewing unread messages.

TypeDefault
booleanfalse

reactionDetailsSort

Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.

TypeDefault
{ created_at: number }reverse chronological order

sortReactions

Comparator function to sort reactions. Should have the same signature as the sort method for a string array.

TypeDefault
(this: ReactionSummary, that: ReactionSummary) => numberchronological order

threadList

If true, indicates that the current MessageList component is part of a Thread.

TypeDefault
booleanfalse

unsafeHTML

If true, renders HTML instead of markdown. Posting HTML is only supported server-side.

TypeDefault
booleanfalse
© Getstream.io, Inc. All Rights Reserved.