const { messages } = useChannelStateContext();
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.
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.
Type | Default |
---|---|
boolean | false |
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:
Type |
---|
object |
disableDateSeparator
If true, disables the injection of date separator UI components in the Channel
MessageList
component.
Type | Default |
---|---|
boolean | false |
disableQuotedMessages
If true, disables the ability for users to quote messages.
Type | Default |
---|---|
boolean | false |
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.
Type | Default |
---|---|
boolean | ChannelStateContextValue[‘hasMore’] |
headerPosition
Position to render the HeaderComponent
in the list.
Type |
---|
number |
hideDeletedMessages
If true, removes the MessageDeleted
components from the list.
Type | Default |
---|---|
boolean | false |
hideNewMessageSeparator
If true, hides the DateSeparator
component that renders when new messages are received in a channel that’s watched but not active.
Type | Default |
---|---|
boolean | false |
internalInfiniteScrollProps
Additional props for the underlying InfiniteScroll component.
Type |
---|
object |
loadingMore
Whether the list is currently loading more items.
Type | Default |
---|---|
boolean | ChannelStateContextValue[‘loadingMore’] |
loadMore
Function called when more messages are to be loaded, provide your own function to override the handler stored in context.
Type | Default |
---|---|
function | ChannelActionContextValue[‘loadMore’] |
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.
Type | Default |
---|---|
component | MessageSimple |
messageActions
Array of allowed message actions (ex: ‘edit’, ‘delete’, ‘reply’). To disable all actions, provide an empty array.
Type | Default |
---|---|
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.
Type | Default |
---|---|
number | 100 |
messages
The messages to render in the list. Provide your own array to override the data stored in context.
Type | Default |
---|---|
array | ChannelStateContextValue[‘messages’] |
noGroupByUser
If true, turns off message UI grouping by user.
Type | Default |
---|---|
boolean | false |
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.
Type | Default |
---|---|
boolean | false |
onMentionsClick
Custom action handler function to run on click on a @mention in a message.
Type | Default |
---|---|
function | ChannelActionContextValue[‘onMentionsClick’] |
onMentionsHover
Custom action handler function to run on hover over a @mention in a message.
Type | Default |
---|---|
function | ChannelActionContextValue[‘onMentionsHover’] |
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} />;
};
Type | Default |
---|---|
(message: StreamMessage, event?: React.BaseSyntheticEvent) => void | ChannelActionContextValue[‘openThread’] |
pinPermissions
The user roles allowed to pin messages in various channel types (deprecated in favor of channelCapabilities
).
Type | Default |
---|---|
object | defaultPinPermissions |
renderText
Custom function to render message text content.
Type | Default |
---|---|
function | renderText |
renderMessages
Custom function to render message text content.
Type | Default |
---|---|
function | defaultRenderMessages |
Parameters
The function receives a single object with the following properties:
Name | Type | Description |
---|---|---|
components | ComponentContextValue | UI components, including possible overrides |
customClasses | object | Object containing custom CSS classnames to override the library’s default container CSS |
lastReceivedMessageId | string | The latest message ID in the current channel |
messageGroupStyles | string[] | An array of potential styles to apply to a grouped message (ex: top, bottom, single) |
messages | Array<ChannelStateContextValue[‘messages’]> | The messages to render in the list |
readData | object | The read state for for messages submitted by the user themselves |
sharedMessageProps | object | Object 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.
Type | Default |
---|---|
function | ChannelActionContextValue[‘retrySendMessage’] |
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.
Type | Default |
---|---|
boolean | false |
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 messagecontext
- configuration params and information forwarded fromprocessMessages
index
- index of the processed message in the original messages arraymessages
- array of messages retrieved from the back-endprocessedMessages
- 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 separatorhideDeletedMessages
- flag determining whether deleted messages would be filtered out during the processinghideNewMessageSeparator
- disables date separator display for unread incoming messageslastRead
: 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.
Type | Default |
---|---|
number | 200 |
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.
Type | Default |
---|---|
boolean | false |
reactionDetailsSort
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.
Type | Default |
---|---|
{ 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.
Type | Default |
---|---|
(this: ReactionSummary, that: ReactionSummary) => number | chronological order |
threadList
If true, indicates that the current MessageList
component is part of a Thread
.
Type | Default |
---|---|
boolean | false |
unsafeHTML
If true, renders HTML instead of markdown. Posting HTML is only supported server-side.
Type | Default |
---|---|
boolean | false |
- Basic Usage
- Message grouping
- Custom message list rendering
- Props
- additionalMessageInputProps
- closeReactionSelectorOnClick
- customMessageActions
- disableDateSeparator
- disableQuotedMessages
- formatDate
- getDeleteMessageErrorNotification
- getFetchReactionsErrorNotification
- getFlagMessageErrorNotification
- getFlagMessageSuccessNotification
- getMarkMessageUnreadErrorNotification
- getMarkMessageUnreadSuccessNotification
- getMuteUserErrorNotification
- getMuteUserSuccessNotification
- getPinMessageErrorNotification
- groupStyles
- hasMore
- headerPosition
- hideDeletedMessages
- hideNewMessageSeparator
- internalInfiniteScrollProps
- loadingMore
- loadMore
- maxTimeBetweenGroupedMessages
- Message
- messageActions
- messageLimit
- messages
- noGroupByUser
- onlySenderCanEdit
- onMentionsClick
- onMentionsHover
- onUserClick
- onUserHover
- openThread
- pinPermissions
- renderText
- renderMessages
- retrySendMessage
- returnAllReadData
- reviewProcessedMessage
- scrolledUpThreshold
- showUnreadNotificationAlways
- reactionDetailsSort
- sortReactions
- threadList
- unsafeHTML