# Indicators

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/channel-list-ui/), [Message list UI](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/message-list-ui/) and [Thread UI](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/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](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/message-list/typing-indicator/). The channel list's skeleton, its next-page spinner and its error banner are on [ChannelListView](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel-list/channel-list-view/).

## 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](https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/connection-status/#when-a-bar-shows) 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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/localization/). [Theme object](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/theme-object/#loadingindicator) lists the keys.

## Example

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

```tsx title="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](https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/connection-status/#replacing-the-indicators) replaces the two network indicators.

## Related hooks

| Hook                                                                                                                               | Returns                                                                     |
| ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [`useNetworkConnectionState()`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/connection-hooks/#usenetworkconnectionstate)       | whether the device has a network, `isOnline`                                |
| [`useSettledWSConnectionHealth()`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/connection-hooks/#usesettledwsconnectionhealth) | whether the socket is healthy, with the display delay applied               |
| [`useMessagePaginator()`](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/loading-data-hooks/#usemessagepaginator)                 | the paginator whose `isLoading` and `lastQueryError` the message list reads |

---

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