MessageList
MessageList renders the loaded messages of the channel or the thread it sits in, in an inverted FlatList, 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 MessageFlashList.
Best practices
- Render it inside a
Channelor aThread. It reads the paginator, the composer's height and the message settings from the contexts they provide, so the same<MessageList />works in both. - Pass
onThreadSelectin a channel. The replies button and the thread reply action call it with the message'sThread, and do nothing without it. - Change how one message looks with the
MessageItemViewoverride, and the actions withmessageActionsonChannel. See Message actions. The list has no prop for either. - Declare the components you pass, such as
FooterComponentandHeaderComponent, outside the component that renders the list, and keepadditionalFlatListPropsstable. - Use
setFlatListRefto reach theFlatList, not arefinadditionalFlatListProps.
Basic usage
import type { Channel as StreamChannel, Thread } from "stream-chat";
import {
Channel,
MessageComposer,
MessageList,
} from "stream-chat-react-native";
type ChannelScreenProps = {
channel: StreamChannel;
onOpenThread: (thread: Thread) => void;
};
export const ChannelScreen = ({
channel,
onOpenThread,
}: ChannelScreenProps) => (
<Channel channel={channel}>
<MessageList onThreadSelect={onOpenThread} />
<MessageComposer />
</Channel>
);In a thread, render it inside <Thread thread={thread}> the same way, without onThreadSelect. How a component of yours reads the paginator the list renders is on Reading state in components.
Message grouping
The list groups consecutive messages from the same user. Each message gets a group style, top, middle, bottom or single, which decides its padding, its bubble corners, and whether the avatar and the footer with the time show: only the last message of a group, bottom or single, shows them, and a message that is still sending or failed keeps its footer.
enableMessageGroupingByUser={false}onChannel, ornoGroupByUseron the list, turns grouping off. Every message then gets an empty group style, so no message counts as the last of its group: no message shows the avatar, and only a message that is sending or failed shows the footer with the time.maxTimeBetweenGroupedMessagesonChannelstarts a new group when two messages are further apart than this many milliseconds.getMessageGroupStyleonChannelreplaces the rule. It receives the message, its neighbors and the date separators around it, and returns the group styles. The default is the exportedgetGroupStyle.
What the list renders
Around the messages, the list renders:
| Part | When | Override key |
|---|---|---|
| The older-end indicator, under the parent message in a thread | the indicator while an older page loads, the parent message always | none, the FooterComponent prop |
| The newer-end indicator and the typing indicator | while a newer page loads, and while someone types | none, the HeaderComponent prop |
StickyHeader |
while the list has messages, unless hideStickyDateHeader is set |
StickyHeader |
ScrollToBottomButton |
while the list isn't at the latest message, or the latest messages aren't loaded | ScrollToBottomButton |
UnreadMessagesNotification |
in a channel, while the last read message is above the screen | UnreadMessagesNotification |
NetworkDownIndicator |
while the device is offline or the connection is down | NetworkDownIndicator |
| The composer's suggestion list | while the composer shows suggestions | AutoCompleteSuggestionList |
The NotificationList of the channel or thread |
while it has notifications | NotificationList |
EmptyStateIndicator with listType="message", in place of the list |
in a channel with no messages | EmptyStateIndicator |
MessageListLoadingIndicator with listType="message", in place of the list |
while the first page loads | MessageListLoadingIndicator |
For each message the list renders the date separator when a day starts, the Message override, and the unread separator under the last read message. A system message renders MessageSystem instead. Floating controls explains when each control shows, and Separators the separators.
Props
MessageListProps makes every prop optional. The props marked "from" a context default to that context's value, so you set them only to override it.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalFlatListProps |
Partial<FlatListProps<MessageListItemWithNeighbours>> |
Props for the inner FlatList. style and contentContainerStyle are merged with the list's own, and accessibilityActions and onAccessibilityAction are combined with the list's scroll-to-bottom action. Any other key replaces the list's prop of the same name, onLayout and onContentSizeChange included. Each item is { message, previousMessage, nextMessage }. |
|
animateLayout |
boolean |
true |
Animates layout changes with a Reanimated layout transition. With false, the list renders the FlatList from MessagesContext instead. |
attachmentPickerStore |
AttachmentPickerStore |
from AttachmentPickerContext |
The attachment picker's state. A touch on the list closes the picker. |
channel |
Channel |
from ChannelContext |
The channel whose messages the list renders. |
client |
StreamChat |
from ChatContext |
The client. |
closePicker |
() => void |
from AttachmentPickerContext |
Closes the attachment picker. |
disabled |
boolean |
from ChannelContext |
true while the channel is frozen. When it turns true, the scroll-to-bottom button hides. |
disableTypingIndicator |
boolean |
from MessagesContext |
Leaves the typing indicator out of the default HeaderComponent. |
FlatList |
typeof FlatList |
from MessagesContext |
The list component the list renders when animateLayout is false. React Native's FlatList unless Channel sets another. |
FooterComponent |
React.ComponentType<{ loadingMore?: boolean }> |
ThreadFooterComponent in a thread, InlineLoadingMoreIndicator otherwise |
Rendered at the older end, the top of the screen, and receives loadingMore. In a thread the default renders the parent message, so a replacement replaces it too. |
HeaderComponent |
React.ComponentType |
the newer-end loading indicator, then the typing indicator | Rendered at the newer end, the bottom of the screen. It receives no props, and a replacement removes the typing indicator unless it renders one. |
hideStickyDateHeader |
boolean |
from ChannelContext |
Turns off the sticky date header. |
inverted |
boolean |
true |
Sets inverted on the FlatList. The list's paging and scroll handling assume an inverted list. |
isLiveStreaming |
boolean |
false |
Tunes the list for many messages a second: list updates are batched to one per frame, the list keeps following new messages from up to 300 points away instead of 10, scroll events arrive every 16 ms, and FlatList's strictMode is on from React Native 0.87. Experimental. |
markRead |
(options?: MarkReadFunctionOptions) => void |
useMarkRead(channel) |
Marks the channel read. In a channel, the list calls it when a new message arrives while the user views the latest messages, and, with updateChannelUnreadState: false, when the user taps the scroll-to-bottom button. The unread notification calls it when closed. |
messageInputFloating |
boolean |
from MessageInputContext |
With true, the list pads its content by the composer's height and lifts its floating parts above the composer. |
messageInputHeightStore |
MessageInputHeightStore |
from MessageInputContext |
The store holding the composer's height. |
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
A theme merged over the current one for the user's own messages. |
noGroupByUser |
boolean |
true when Channel has enableMessageGroupingByUser={false} |
Turns grouping off. The list combines it with the Channel setting, so false doesn't turn grouping on when Channel turned it off. |
onListScroll |
ScrollViewProps['onScroll'] |
Called with every scroll event. | |
onThreadSelect |
(thread: Thread) => void |
Called with the message's Thread when the user presses a message's replies button or picks the thread reply action. Render it in a <Thread thread={thread}>. |
|
pendingUploadsEnabled |
boolean |
messageComposer.attachments.pendingUploadsEnabled |
With true, the list's NotificationList leaves out the notifications of canceled uploads. |
readEvents |
boolean |
from OwnCapabilitiesContext |
Whether the user can send read events. Without them the unread notification shows only when the channel counts unread messages locally. |
setFlatListRef |
(ref: FlatListType<MessageListItemWithNeighbours> | null) => void |
Receives the ref of the inner FlatList. |
|
thread |
Thread |
from ThreadContext |
The thread whose replies the list renders. |
MessageListItemWithNeighbours, AttachmentPickerStore and MessageInputHeightStore aren't exported, so type a callback that receives them through MessageListProps, for example MessageListProps['setFlatListRef'].
Parts the list renders
These are the defaults of the override keys above. Replace one with WithComponents; Overriding components explains how. StickyHeader and UnreadMessagesNotification aren't exported, so a replacement can't wrap them.
InlineDateSeparator
Renders the date above the first message of a day. The text comes from the timestamp.InlineDateSeparator translation key, and screen readers get the full date.
| Prop | Type | Default | Description |
|---|---|---|---|
date |
Date |
The date to show. |
InlineUnreadIndicator
Renders the unread separator under the last read message: "5 new messages", or "Unread Messages" without a count.
| Prop | Type | Default | Description |
|---|---|---|---|
unreadCount |
number |
The number of unread messages, from the paginator's unread snapshot. |
StickyHeader and DateHeader
StickyHeader receives the date of the topmost message on screen, formats it with the timestamp.StickyHeader translation key, and renders the DateHeader override with the result. It renders nothing without a date.
| Prop | Type | Default | Description |
|---|---|---|---|
date |
Date |
The date to show. | |
dateString |
string | number |
Text shown in place of the formatted date. It's read only when date changes. |
DateHeader renders the label.
| Prop | Type | Default | Description |
|---|---|---|---|
dateString |
string | number |
The text to show. |
UnreadMessagesNotification
Renders "5 unread", or "Unread Messages" without a count, with a close button. The count is the paginator's unread snapshot. The list passes markRead and an onCloseHandler that marks the channel read and hides the notification.
| Prop | Type | Default | Description |
|---|---|---|---|
markRead |
(options?: MarkReadFunctionOptions) => void |
Marks the channel read. Called by the close button when there's no onCloseHandler. |
|
onCloseHandler |
() => void |
calls markRead |
Called when the user presses the close button. |
onPressHandler |
() => Promise<void> |
jumps to the first unread message | Called when the user presses the notification. A failed jump publishes an error notification. |
InlineLoadingMoreIndicator and InlineLoadingMoreRecentIndicator
The spinners the default FooterComponent and HeaderComponent render. They aren't override keys; pass your own FooterComponent or HeaderComponent instead.
| Prop | Type | Default | Description |
|---|---|---|---|
loadingMore |
boolean |
InlineLoadingMoreIndicator renders a spinner while true. |
| Prop | Type | Default | Description |
|---|---|---|---|
loadingMoreRecent |
boolean |
InlineLoadingMoreRecentIndicator renders a spinner while true. |
NetworkDownIndicator takes no props. It shows "Waiting for network..." while the device is offline and "Reconnecting..." while the connection to Stream is down.
Related hooks
| Hook | Returns |
|---|---|
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, such as its items |
useMessagesContext() |
the message settings of Channel or Thread. See MessagesContext |
useMessageContext() |
the message and its handlers, inside each message the list renders. See MessageContext |
useMarkRead(channel) |
the markRead callback the list uses by default |
useTypingString() |
the "Cleo is typing" text of the list's channel or thread |
The paginator's API is on Message paginator, and jumping to a message on Message paginators.