Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

Indicators

The SDK's loading, error, empty-state and connection indicators. This page lists where each one renders, how to replace it, and its props. The Components pages show them in context: Channel list UI, Message list UI and Thread UI.

Best practices

  • Replace an indicator through its override key, with WithComponents. The lists don't take indicators as props.
  • One component can sit behind several keys. The message list's loading state is the MessageListLoadingIndicator key, whose default is LoadingIndicator, and the thread list has keys of its own, such as ThreadListEmptyPlaceholder. Replace the key of the place you want to change.
  • Switch on listType in a replacement of EmptyStateIndicator or LoadingErrorIndicator. The same key serves the channel list and the message list.
  • Keep a loading or empty state the size of what it stands in for, so the layout doesn't jump when the data arrives.
  • The message lists always render NetworkDownIndicator and let it decide whether to show anything. A replacement has to read the connection state and return null itself.

Available components

Every component here is exported from stream-chat-react-native and stream-chat-expo.

Component What it shows Where the SDK renders it Override key
LoadingIndicator A spinner and a loading text MessageList and MessageFlashList while their first page loads, and the thread list's next page MessageListLoadingIndicator, for the message lists
LoadingErrorIndicator A loading error, with a retry press ChannelList when its first page fails, Channel when its first page of messages fails, and Thread when its first page of replies fails LoadingErrorIndicator
EmptyStateIndicator An icon and a line for an empty list ChannelList and the message lists of a Channel with no items, and the thread list's default empty state EmptyStateIndicator, for the channel list and message lists
NetworkDownIndicator "Waiting for network..." or "Reconnecting..." The top of MessageList and MessageFlashList NetworkDownIndicator
ChannelListHeaderNetworkDownIndicator "Reconnecting..." The top of ChannelList, while the device or the socket is offline ChannelListHeaderNetworkDownIndicator
LoadingDots Three dots that fade in turn The typing indicators of the message list and the channel list None

The typing indicator has a page of its own, TypingIndicator. The channel list's skeleton, its next-page spinner and its error banner are on ChannelListView.

UI customization

LoadingIndicator

The message lists render the MessageListLoadingIndicator key with listType="message" while the paginator loads and there are no messages to show yet. Its default, LoadingIndicator, then shows "Loading messages...". The thread list's default ThreadListLoadingMoreIndicator renders LoadingIndicator with listType="threads", a spinner without text, while the next page of threads loads.

The indicator fills its container and centers a 20-point Spinner above the text. It has the progressbar role, and announces itself through a polite live region on Android.

LoadingErrorIndicator

ChannelList renders the LoadingErrorIndicator key with listType="channel" in place of the list when its query fails before it has channels, with retry reloading the list. Channel renders it with listType="message" in place of its children when the first page of messages fails, and Thread with listType="thread" when the first page of replies fails, each with retry loading it again.

The default shows "Error loading channel list...", "Error loading messages for this channel..." or "Error loading replies...", and the whole indicator is a button that calls retry. With no listType, it shows "Error loading" and isn't pressable. It doesn't show the error's message.

EmptyStateIndicator

ChannelList renders the EmptyStateIndicator key with listType="channel" when it has loaded and has no channels. The message lists render it with listType="message" when a Channel has no messages. Inside a Thread they render nothing, because the parent message shows above the replies.

listType The default shows
'channel' An empty bubble and "No conversations yet"
'message' An empty bubble and the indicators.emptyState.noChats.text translation
'threads' An empty bubble and "Reply to a message to start a thread"
none "No items exist"

The thread list's ThreadListEmptyPlaceholder key defaults to a component that renders EmptyStateIndicator with listType="threads" directly, so an EmptyStateIndicator override doesn't reach the thread list. Override ThreadListEmptyPlaceholder for that.

NetworkDownIndicator and ChannelListHeaderNetworkDownIndicator

NetworkDownIndicator takes no props. It reads useNetworkConnectionState() and useSettledWSConnectionHealth(), and renders:

  • "Waiting for network..." while the device reports it's offline;
  • "Reconnecting..." once the socket has been down for client.wsConnection.config.offlineNotificationDisplayDelayMs;
  • nothing otherwise.

It's positioned absolutely at the top of the message list, over the messages.

ChannelListHeaderNetworkDownIndicator takes no props and always renders "Reconnecting...". ChannelList renders it in the same two cases, and not while its first page loads. Connection status explains the timing.

LoadingDots

LoadingDots renders numberOfDots dots that fade one after the other, and is hidden from screen readers. It has no override key; the typing indicators render it directly.

LoadingProps

Prop Type Default Description
listType 'channel' | 'message' | 'threads' | 'default' Picks the text: "Loading channels...", "Loading messages...", none for 'threads', or "Loading...".
loadingText string Shown in place of the text listType picks.

LoadingErrorProps

Prop Type Default Description
listType 'channel' | 'message' | 'thread' | 'default' Picks the text, and whether the indicator is a retry button.
retry () => void a no-op Called when the user presses the indicator, for 'channel' and 'message'.
error Error The error that happened. The SDK passes it; the default doesn't render it.
loadNextPage () => Promise<void> Passed by ChannelList. The default doesn't call it.

EmptyStateProps

Prop Type Default Description
listType 'channel' | 'message' | 'threads' | 'default' The list it's shown in. See above.

LoadingDots props

LoadingDots doesn't export a props type.

Prop Type Default Description
diameter number 5 The size of each dot, in points.
duration number 1500 The length of one cycle, in milliseconds.
numberOfDots number 3 How many dots.
spacing number the theme's loadingDots.spacing The space between two dots.
style StyleProp<ViewStyle> Applied to the row, after the theme.

Theming

Component Theme keys
LoadingIndicator loadingIndicator.container, loadingIndicator.loadingText, and spinner for its spinner
LoadingErrorIndicator loadingErrorIndicator.container, errorText, retryText
EmptyStateIndicator emptyStateIndicator.channelContainer, channelTitle, messageContainer, messageTitle
NetworkDownIndicator messageList.errorNotification, messageList.errorNotificationText
ChannelListHeaderNetworkDownIndicator channelListHeaderErrorIndicator.container, channelListHeaderErrorIndicator.errorText
LoadingDots loadingDots.container, loadingDots.spacing

The texts are translation keys of the indicators.* and common.* namespaces; see Localization. Theme object lists the keys.

Example

An empty channel and an empty channel list of your own, from one EmptyStateIndicator:

src/EmptyStates.tsx
import { Text, View } from "react-native";
import { EmptyStateIndicator } from "stream-chat-react-native";
import type {
  ComponentOverrides,
  EmptyStateProps,
} from "stream-chat-react-native";

const AppEmptyState = ({ listType }: EmptyStateProps) => {
  if (listType === "message") {
    return (
      <View style={{ alignItems: "center", flex: 1, justifyContent: "center" }}>
        <Text>No messages yet. Say hi!</Text>
      </View>
    );
  }
  if (listType === "channel") {
    return (
      <View style={{ alignItems: "center", padding: 24 }}>
        <Text>Join a channel to start.</Text>
      </View>
    );
  }
  return <EmptyStateIndicator listType={listType} />;
};

// Declared outside the component, so WithComponents gets the same object on every render.
export const emptyStateOverrides: ComponentOverrides = {
  EmptyStateIndicator: AppEmptyState,
};

Pass emptyStateOverrides to a WithComponents around ChannelList and Channel. Connection status replaces the two network indicators.

Hook Returns
useNetworkConnectionState() whether the device has a network, isOnline
useSettledWSConnectionHealth() whether the socket is healthy, with the display delay applied
useMessagePaginator() the paginator whose isLoading and lastQueryError the message list reads