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. 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.
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:
LoadingErrorIndicatorgetslistType('channel','message'or'thread', which picks the text),errorandretry.EmptyStateIndicatorgetslistType('channel','message'or'threads').MessageListLoadingIndicatorrendersLoadingIndicatorwithlistType='message', and the next-page indicator of the thread list renders it withlistType='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
- The query fails, after the paginator's retries: a channel list paginator retries a failed query three times.
- The paginator records the error as
lastQueryErrorinpaginator.state, and the list rendersLoadingErrorIndicatorin place of its rows. - 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.
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 for the notifications the SDK publishes.
Other lists
- Who reacted.
MessageUserReactionsloads the reactions of a message, 25 at a time, and renders its list once the first page has loaded. A failed load publishes the notificationapi: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()andusePollOptionVotesPagination()returnloadinganderrorfor 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 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 |