MessageFlashList
MessageFlashList renders the same messages as MessageList, on FlashList from @shopify/flash-list instead of React Native's FlatList. It recycles cells between messages of the same shape, such as text only or with a gallery, instead of creating one per message. Inside a Thread it renders thread.messagePaginator, otherwise channel.messagePaginator. Message list UI compares the two lists.
MessageFlashList is experimental: its implementation can change between releases. Test it with your app's messages before you ship it.
Best practices
- Install
@shopify/flash-list2.1.0 or later. It's an optional peer dependency, andMessageFlashListthrows when it isn't installed. - Use
MessageListunless profiling shows the list is the bottleneck. Performance covers when it helps. - Mind that the list isn't inverted:
HeaderComponentis the older end, at the top of the screen, andFooterComponentthe newer end, at the bottom.MessageListhas them the other way round. - Keep custom message components light, and their state out of component state where you can: a recycled cell keeps its component instance when it moves to another message.
- Pass
onThreadSelectin a channel, as withMessageList.
Basic usage
import type { Channel as StreamChannel, Thread } from "stream-chat";
import {
Channel,
MessageComposer,
MessageFlashList,
} from "stream-chat-react-native";
type ChannelScreenProps = {
channel: StreamChannel;
onOpenThread: (thread: Thread) => void;
};
export const ChannelScreen = ({
channel,
onOpenThread,
}: ChannelScreenProps) => (
<Channel channel={channel}>
<MessageFlashList onThreadSelect={onOpenThread} />
<MessageComposer />
</Channel>
);In a thread, render <MessageFlashList /> inside <Thread thread={thread}>.
How it differs from MessageList
Both lists read the same contexts, render the same parts and use the same override keys. They differ in:
MessageList |
MessageFlashList |
|
|---|---|---|
| Extra list props | additionalFlatListProps |
additionalFlashListProps |
| Older end, at the top | FooterComponent |
HeaderComponent |
| Newer end, at the bottom | HeaderComponent |
FooterComponent |
| A list shorter than the screen | loads older pages until the messages fill the screen | loads older pages as the user scrolls up |
| Layout animation | animateLayout, on by default |
none |
The FlatList from MessagesContext |
used with animateLayout={false} |
not used |
inverted |
a prop, true by default |
not a prop: the list renders oldest first |
Each item is a LocalMessage. The list tells FlashList the item type of each message, such as message-with-gallery, message-with-text or deleted-message, so a cell is recycled only between messages of that type.
Props
MessageFlashListProps makes every prop optional. The props marked "from" a context default to that context's value.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalFlashListProps |
Partial<FlashListProps<LocalMessage>> |
Props for the inner FlashList. 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. |
|
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 FooterComponent. |
FooterComponent |
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. |
hasPendingInitialTargetLoad |
() => boolean |
from ChannelContext |
Set by Channel and Thread, and returns true while a jump to a message, such as the one the channel was opened at, is pending. The list doesn't scroll to the newest message meanwhile. |
HeaderComponent |
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. |
hideStickyDateHeader |
boolean |
from ChannelContext |
Turns off the sticky date header. |
isLiveStreaming |
boolean |
false |
Tunes the list for many messages a second: list updates are batched to one per frame, and scroll events arrive every 16 ms. 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: FlashListRef<LocalMessage> | null) => void |
Receives the ref of the inner FlashList. |
|
thread |
Thread |
from ThreadContext |
The thread whose replies the list renders. |
Related hooks
The hooks are the same as for MessageList: see MessageList.