# 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](https://getstream.io/chat/docs/sdk/react-native/ui/components/message-list-ui/) explains what it shows, and how it differs from [`MessageFlashList`](https://getstream.io/chat/docs/sdk/react-native/component-reference/message-list/messageflashlist/).

## Best practices

- Render it inside a `Channel` or a `Thread`. 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 `onThreadSelect` in a channel. The replies button and the thread reply action call it with the message's `Thread`, and do nothing without it.
- Change how one message looks with the `MessageItemView` override, and the actions with `messageActions` on `Channel`. See [Message actions](https://getstream.io/chat/docs/sdk/react-native/ui/components/actions/#message-actions). The list has no prop for either.
- Declare the components you pass, such as `FooterComponent` and `HeaderComponent`, outside the component that renders the list, and keep `additionalFlatListProps` stable.
- Use `setFlatListRef` to reach the `FlatList`, not a `ref` in `additionalFlatListProps`.

## Basic usage

```tsx title="src/ChannelScreen.tsx"
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 [Providers and context hooks](https://getstream.io/chat/docs/sdk/react-native/ui/providers-and-context-hooks/#which-owner-a-hook-reads).

## 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}` on `Channel`, or `noGroupByUser` on 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.
- `maxTimeBetweenGroupedMessages` on `Channel` starts a new group when two messages are further apart than this many milliseconds.
- `getMessageGroupStyle` on `Channel` replaces the rule. It receives the message, its neighbors and the date separators around it, and returns the group styles. The default is the exported `getGroupStyle`.

## 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](https://getstream.io/chat/docs/sdk/react-native/ui/components/message-list-ui/#floating-controls) explains when each control shows, and [Separators](https://getstream.io/chat/docs/sdk/react-native/ui/components/message-list-ui/#separators) the separators.

## Scrolling to new messages

While the list shows the newest messages, it stays on them as new ones arrive. A message the user sends scrolls the list to the newest message wherever it was scrolled, in a channel and in a thread. A thread that opens before its replies load follows them as the first page arrives. While the newest messages aren't loaded, the list doesn't follow anything and shows `ScrollToBottomButton`.

## 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](https://getstream.io/chat/docs/sdk/react-native/ui/components/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 unread messages" or "1 unread message", 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`. The list passes no date while the oldest message of the list is on screen, where the inline date separator shows the same day, and keeps the date it had while the topmost message is a deleted one.

| 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. It doesn't pass `onPressHandler`, and the component isn't exported, so the props below describe what it accepts, not something an app can set.

| 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](https://getstream.io/chat/docs/sdk/react-native/component-reference/message-list/messagescontext/)               |
| `useMessageContext()`                | the message and its handlers, inside each message the list renders. See [MessageContext](https://getstream.io/chat/docs/sdk/react-native/component-reference/message/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](https://getstream.io/chat/docs/sdk/react-native/reference/state/message-paginator/), and jumping to a message on [Message paginators](https://getstream.io/chat/docs/sdk/react-native/state/loading-data/message-paginators/).

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/component-reference/message-list/messagelist/](https://getstream.io/chat/docs/sdk/react-native/component-reference/message-list/messagelist/).