# List states UI

The SDK's lists show a state of their own while they load, when a load fails, and when they're empty. Use this page to know which states each list has, which ones you can replace, and what happens when a first page fails to load.

## States and their keys

Each cell names the override key that replaces the state. "Built in" means the list renders a fixed component that no key replaces. "None" means the list has no such state. To register a replacement, see [How overrides merge](https://getstream.io/chat/docs/sdk/react-native/ui/components/overriding-components/#how-overrides-merge). [ComponentsContext](https://getstream.io/chat/docs/sdk/react-native/component-reference/channel/componentscontext/) lists each key's default.

| List                                                   | First page loading                                          | First page failed                                 | Next page loading                                                          | Empty                                            |
| ------------------------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------ |
| `ChannelList`                                          | `ChannelListLoadingIndicator`, with `Skeleton` for each row | `LoadingErrorIndicator`                           | `ChannelListFooterLoadingIndicator`                                        | `EmptyStateIndicator`                            |
| `MessageList`, `MessageFlashList`                      | `MessageListLoadingIndicator`                               | `Channel` renders `LoadingErrorIndicator` instead | The list's `FooterComponent` or `HeaderComponent` prop, for older messages | `EmptyStateIndicator`, not in a thread           |
| `Thread`                                               | None                                                        | `Thread` renders `LoadingErrorIndicator` instead  | None                                                                       | None                                             |
| `ThreadList`                                           | `ThreadListLoadingIndicator`                                | None                                              | `ThreadListLoadingMoreIndicator`                                           | `ThreadListEmptyPlaceholder`                     |
| `ChannelMemberList`, `ChannelAddMembersForm`           | Built in: placeholder rows                                  | A notification                                    | Built in: a spinner                                                        | Built in: "No members found", or "No user found" |
| `PinnedMessageList`, `MediaList`, `FileAttachmentList` | Built in: placeholders                                      | A notification                                    | Built in: a spinner                                                        | Built in: a message with an icon                 |
| `MessageUserReactions`, the list of who reacted        | None: the list shows once the first page loaded             | A notification                                    | None                                                                       | None                                             |
| Poll answers and a poll option's votes                 | None                                                        | None                                              | None                                                                       | None                                             |
| The suggestion list in the composer                    | None                                                        | None                                              | None                                                                       | None                                             |

Two states of the channel list sit outside the table: the connection banner and the error banner over the top of the list. They're in [The channel list](#the-channel-list).

`ThreadList` has no first-page error state, and `Thread` has no first-page loading state of its own: it reloads a stale thread while its message list shows the parent message. When a thread holds no replies and its first page loads, the message list renders `MessageListLoadingIndicator`. The default components get these props:

- `LoadingErrorIndicator` gets `listType` (`'channel'`, `'message'` or `'thread'`, which picks the text), `error` and `retry`.
- `EmptyStateIndicator` gets `listType` (`'channel'`, `'message'` or `'threads'`).
- `MessageListLoadingIndicator` renders `LoadingIndicator` with `listType='message'`, and the next-page indicator of the thread list renders it with `listType='threads'`.

## The channel list

| State                                       | What the list renders                                                   | Override key                              |
| ------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------- |
| Loading the first page                      | Placeholder rows, `numberOfSkeletons` of them (8 by default)            | `ChannelListLoadingIndicator`, `Skeleton` |
| Loading the next page                       | A spinner after the last row                                            | `ChannelListFooterLoadingIndicator`       |
| Refreshing                                  | The pull-to-refresh spinner above the rows                              | None                                      |
| No channels                                 | "No conversations yet". The component gets `listType="channel"`.        | `EmptyStateIndicator`                     |
| The first page failed                       | "Error loading channel list...". Tapping it loads the first page again. | `LoadingErrorIndicator`                   |
| A query failed while channels are on screen | A banner over the top of the list. Tapping it refreshes the list.       | `ChannelListHeaderErrorIndicator`         |
| No network, or the connection is down       | A "Reconnecting..." banner over the top of the list                     | `ChannelListHeaderNetworkDownIndicator`   |

### When the first page fails

1. The query fails, after the paginator's retries: a channel list paginator retries a failed query three times.
2. The paginator records the error as `lastQueryError` in `paginator.state`, and the list renders `LoadingErrorIndicator` in place of its rows.
3. Tapping it calls `ChannelPaginator.reload()` on the list's paginator, which loads the first page again.

With offline support on, a failure while the list has channels isn't recorded, so the channels from the local database stay on screen without an error. When a query fails while channels are on screen, for example the next page or a pull to refresh, the list keeps the rows and shows the banner over the top, and tapping it refreshes the list. A pull within 5 seconds of the last refresh is ignored, unless the last query failed. Neither the list nor the paginator publishes a notification for a failed query.

After a reconnect, `client.connectionRecovery` re-queries the channels the list has loaded by itself. The list doesn't show a separate state for it.

## The message lists and the thread

| State                      | What the list renders                                                                                                                                         | Override key                  |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| Loading the first page     | A spinner with "Loading messages..." in place of the list                                                                                                     | `MessageListLoadingIndicator` |
| Loading older messages     | A spinner at the older end. In a thread, under the parent message.                                                                                            | None (the list's prop)        |
| Loading newer messages     | A spinner at the newer end                                                                                                                                    | None (the list's prop)        |
| A channel with no messages | `EmptyStateIndicator` with `listType="message"`. A thread with no replies shows its parent message instead.                                                   | `EmptyStateIndicator`         |
| The first page failed      | `Channel` renders `LoadingErrorIndicator` in place of its children, "Error loading messages for this channel...". Tapping it loads the latest messages again. | `LoadingErrorIndicator`       |

A failed load of older or newer messages is recorded as `lastQueryError` in the paginator's state, and the list stays as it is. The next scroll to that end tries again.

`Channel` renders the error state when the message list holds no messages and its query failed. It replaces everything inside `Channel`, including a header you render as a child. In a thread, `Thread` renders `LoadingErrorIndicator` with `listType="thread"`, "Error loading replies...", when the first page of replies fails, and tapping it loads the replies again.

## The thread list

| State                  | What the list renders                  | Override key                     |
| ---------------------- | -------------------------------------- | -------------------------------- |
| Loading the first page | Ten placeholder rows                   | `ThreadListLoadingIndicator`     |
| Loading the next page  | A spinner after the last row           | `ThreadListLoadingMoreIndicator` |
| No threads             | "Reply to a message to start a thread" | `ThreadListEmptyPlaceholder`     |

A reload keeps the loaded threads on screen until the new ones arrive. When a query fails, the list keeps what it had loaded, or shows the empty placeholder if it had nothing. The error is recorded as `lastQueryError` in `client.threads.paginator.state`. Only the unread banner shows a failure, when the reload it started fails: see [The unread banner](https://getstream.io/chat/docs/sdk/react-native/ui/components/thread-list-ui/#the-unread-banner).

## Channel details lists

The lists in channel details, which `ChannelDetails` and its screens render, load their items through search sources: the members, the users to add as members, the pinned messages, the photos and videos, and the files. Each list creates its source when it opens, and loads the next page as the user scrolls.

| List                    | While it loads    | When it's empty                                                                                              | When a query fails                                           |
| ----------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
| `ChannelMemberList`     | Placeholder rows  | "No members found", for a search without a match                                                             | The notification `api:channel:query-members:failed`          |
| `ChannelAddMembersForm` | Placeholder rows  | "No user found"                                                                                              | The notification `api:channel:query-users:failed`            |
| `PinnedMessageList`     | Placeholder rows  | "No pinned messages", with a hint to long-press a message; "No pinned messages" for a search without a match | The notification `api:channel:query-pinned-messages:failed`  |
| `MediaList`             | Placeholder tiles | "No photos or videos"                                                                                        | The notification `api:channel:query-media:failed`            |
| `FileAttachmentList`    | Placeholder rows  | "No files"                                                                                                   | The notification `api:channel:query-file-attachments:failed` |

While a next page loads, each list shows a spinner after its last item. A failed query shows an error notification, "Failed to load pinned messages" for example, in the notification list of the screen, and leaves the list as it was. The lists have no retry control: scrolling to the end of the list loads the next page again. See [Notifications UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/#what-the-sdk-publishes) for the notifications the SDK publishes.

## Other lists

- **Who reacted.** `MessageUserReactions` loads the reactions of a message, 25 at a time, and renders its list once the first page has loaded. A failed load publishes the notification `api:message:reactions:fetch:failed`, "Error fetching reactions", and the list stays empty.
- **A poll's answers and a poll option's votes.** The lists load the next page as the user scrolls and render no loading, empty or error state of their own. `usePollAnswersPagination()` and `usePollOptionVotesPagination()` return `loading` and `error` for a list of your own.
- **The suggestion list in the composer.** It loads more suggestions as the user scrolls, and shows only while it has suggestions, so it has none of these states.

## Replacing a state in one list only

`EmptyStateIndicator` and `LoadingErrorIndicator` are read by more than one list, so a replacement registered around the whole app replaces them everywhere. The channel list renders `EmptyStateIndicator` with `listType="channel"` and the message list with `listType="message"`, and `LoadingErrorIndicator` gets `listType` `'channel'`, `'message'` or `'thread'`. A replacement checks `listType` and renders the default for the lists it leaves alone, as the example in [Replacing parts](https://getstream.io/chat/docs/sdk/react-native/ui/components/channel-list-ui/#replacing-parts) on Channel list UI does. To replace a state for one list only, register it in a `WithComponents` around that list only:

| Where the list renders            | Wrap                                        |
| --------------------------------- | ------------------------------------------- |
| The channel list                  | The `ChannelList`                           |
| The message list and the composer | The `Channel`, or the `Thread` for a thread |
| The thread list                   | The `ThreadList`                            |

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/ui/components/list-states-ui/](https://getstream.io/chat/docs/sdk/react-native/ui/components/list-states-ui/).