# Indicators

The SDK's loading, error, empty-state, pagination and typing indicators. This page lists where each one renders, how to replace it, and its props. The Components pages show them in context: [List states UI](https://getstream.io/chat/docs/sdk/react/ui/components/list-states-ui/), [Message list UI](https://getstream.io/chat/docs/sdk/react/ui/components/message-list-ui/) and [Channel UI](https://getstream.io/chat/docs/sdk/react/ui/components/channel-ui/#typing-indicator).

## Best practices

- Replace an indicator through its override key, with `WithComponents`. The lists don't take indicators as props.
- The channel list has keys of its own, such as `EmptyListIndicator`. An `EmptyStateIndicator` override doesn't reach it.
- Keep a loading or empty state the size of what it stands in for, so the layout doesn't jump when the data arrives.
- Treat `TypingIndicator`, at the bottom of the message list, and `TypingIndicatorHeader`, in the header, as two separate parts.
- To change every spinner at once, replace the `IconLoading` icon: `LoadingIndicator` renders it.

## Available components

Every component here imports from `stream-chat-react`.

| Component                | What it shows                                                                               | Where the SDK renders it                                                                                   | Override key                                   |
| ------------------------ | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `EmptyStateIndicator`    | A message for an empty list                                                                 | `MessageList` and `VirtualizedMessageList` with no messages. The channel list's default empty state.       | `EmptyStateIndicator`, for the message lists   |
| `ListLoadErrorIndicator` | An icon, "Couldn't load conversations" or "Couldn't load threads", and a "Try again" button | The channel list and the thread list, in place of the list when its first page failed to load              | `ListLoadErrorIndicator`, for both lists       |
| `LoadingChannels`        | Five placeholder rows                                                                       | The channel list and the thread list, while they load their first page                                     | `FirstPageLoadingIndicator`, for both lists    |
| `LoadingChannel`         | A placeholder header, three messages and a composer                                         | Nowhere. Render it yourself while a channel loads.                                                         | None                                           |
| `LoadingErrorIndicator`  | "Error:" and the error's message                                                            | Nowhere                                                                                                    | None                                           |
| `LoadingIndicator`       | A spinner                                                                                   | The message lists while they load a page, the thread list's next page, upload previews, and other spinners | `LoadingIndicator`, for the parts listed below |
| `LoadMoreButton`         | A "Load more" button, with a spinner while loading                                          | Only inside `LoadMorePaginator`                                                                            | None                                           |
| `LoadMorePaginator`      | Its children and a `LoadMoreButton`                                                         | Nowhere. The channel list loads its next page on scroll.                                                   | None                                           |
| `TypingIndicator`        | The avatars of the people typing and animated dots                                          | The bottom of `MessageList`, and of `VirtualizedMessageList` when the key is set                           | `TypingIndicator`                              |
| `TypingIndicatorHeader`  | "Cleo is typing" and animated dots                                                          | The subtitle of `ChannelHeader` and `ThreadHeader`                                                         | None                                           |

## UI customization

### EmptyStateIndicator

The message lists render the `EmptyStateIndicator` override with `listType="message"` in a channel and `listType="thread"` in a thread. The default shows "Send a message to start the conversation", or `messageText` when passed, and nothing in a thread.

The other lists have their own keys:

- the channel list renders `EmptyListIndicator`, which receives no props and defaults to `<EmptyStateIndicator listType="channel" />`, "No conversations yet";
- the thread list renders `ThreadListEmptyPlaceholder`.

### FirstPageLoadingIndicator and ListLoadErrorIndicator

`FirstPageLoadingIndicator` gets no props and defaults to `LoadingChannels`. `ListLoadErrorIndicator` gets [`ListLoadErrorIndicatorProps`](#listloaderrorindicatorprops). Its default is the exported `<ListLoadErrorIndicator/>`, with `listType` set to `"channel"` or `"thread"`. [List states UI](https://getstream.io/chat/docs/sdk/react/ui/components/list-states-ui/) shows what each list renders and what `retry` calls.

While a retry runs, the list keeps the error view on screen with `isRetrying` set to `true`, so a focused "Try again" button isn't swapped out and keeps the focus. The default button sets `aria-busy` and `aria-disabled` meanwhile, not `disabled`, which would take the focus away. When the retry fails, the view stays and gets the new error as `error`. When it loads the first page, screen readers hear "Conversations loaded" or "Threads loaded".

The view's title and the list's error notification read the same translation key, `channelList.loadError.text` or `threadList.loadError.text`, so overriding that key changes both.

### LoadingErrorIndicator

`LoadingErrorIndicator` renders "Error: " and `error.message`, and nothing without an error. The SDK doesn't render it, and it has no override key. Render it yourself where a load of your own can fail. A channel list or thread list whose first page failed renders `ListLoadErrorIndicator` instead: see [FirstPageLoadingIndicator and ListLoadErrorIndicator](#firstpageloadingindicator-and-listloaderrorindicator).

### LoadingIndicator

These parts render the `LoadingIndicator` override: `VirtualizedMessageList` while it loads a page of messages, `MessageList` while it loads an older page (its [`loadingMore`](https://getstream.io/chat/docs/sdk/react/component-reference/message-list/messagelist/#props) prop changes when), the thread list's default `ThreadListLoadingIndicator`, an attachment preview while the server confirms its upload, and a video attachment while its player loads. The SDK passes it no props that change how it looks.

The key also reaches `<LoadMoreButton/>`, the unseen threads banner, the poll answer and vote lists, the image gallery's thumbnails while they load, the lists in channel details, and the reactions dialog while it loads the users who reacted. That dialog's default is `MessageReactionsDetailLoadingIndicator`, a skeleton of three rows, not a spinner. The channel list's next page is the exception: its default `LoadingNextPageIndicator` renders the SDK's `LoadingIndicator`, so replace `LoadingNextPageIndicator` there. Every spinner, through the key or not, renders the `IconLoading` icon, so replacing that icon changes every spinner. [Icons](https://getstream.io/chat/docs/sdk/react/ui/components/overriding-components/#icons) shows how.

### LoadMorePaginator and LoadMoreButton

`LoadMorePaginator` renders its children and, while `hasNextPage` is `true`, a `LoadMoreButton` that calls `loadNextPage`. Use them for a list of your own. `ChannelList` has no paginator prop: it loads the next page when it's scrolled to the bottom, and shows `LoadingNextPageIndicator` meanwhile.

### TypingIndicator

`MessageList` passes the `TypingIndicator` override `isMessageListScrolledToBottom` and `scrollToBottom`. It shows only while the list is scrolled to the latest message, and scrolls the list down when it appears. `VirtualizedMessageList` has no default: it renders a typing indicator only when the `TypingIndicator` key is set.

It reads `channel.state.typing`, leaves out the current user, and renders nothing when typing events are off for the channel. Inside a `Thread`, it shows the people typing a reply in that thread. [Typing indicators](https://getstream.io/chat/docs/sdk/react/features/messaging/typing-indicators/) covers the feature end to end.

### TypingIndicatorHeader

`TypingIndicatorHeader` replaces the subtitle of `ChannelHeader` and `ThreadHeader` while someone is typing. It has no override key: a custom header or a custom `Subtitle` renders it itself. It reads `channel.state.typing`. Without `threadList`, it shows typing in the channel. With `threadList`, it shows typing in the replies of the `Thread` around it.

## EmptyStateIndicatorProps

| Prop          | Type                                 | Default | Description                                                                                         |
| ------------- | ------------------------------------ | ------- | --------------------------------------------------------------------------------------------------- |
| `listType`    | `"channel" \| "message" \| "thread"` |         | The list it's shown in. With no `listType`, the default shows "No items exist".                     |
| `messageText` | `string`                             |         | For `listType="message"`, the text to show instead of the default. It's passed through translation. |

## ListLoadErrorIndicatorProps

The props the lists pass to the `ListLoadErrorIndicator` override.

| Prop         | Type         | Default  | Description                                                                  |
| ------------ | ------------ | -------- | ---------------------------------------------------------------------------- |
| `error`      | `Error`      | required | The error of the failed query.                                               |
| `isRetrying` | `boolean`    | required | `true` while a retry runs. The list keeps the component on screen meanwhile. |
| `retry`      | `() => void` | required | Queries the first page again. Does nothing while a retry runs.               |

The exported `<ListLoadErrorIndicator/>` takes `DefaultListLoadErrorIndicatorProps`: these props and `listType`, `"channel"` or `"thread"`, which picks its text. To render it inside an override, pass the props on and set `listType`.

The default view has these classes:

| Class                                     | Element                                                             |
| ----------------------------------------- | ------------------------------------------------------------------- |
| `str-chat__list-load-error`               | The root, in both lists                                             |
| `str-chat__channel-list-load-error`       | The root, in the channel list                                       |
| `str-chat__thread-list-load-error`        | The root, in the thread list                                        |
| `str-chat__list-load-error__icon`         | The icon                                                            |
| `str-chat__list-load-error__content`      | The wrapper of the title and the button                             |
| `str-chat__list-load-error__title`        | The title, "Couldn't load conversations" or "Couldn't load threads" |
| `str-chat__list-load-error__retry-button` | The "Try again" button                                              |

## LoadingErrorIndicatorProps

| Prop    | Type            | Default | Description                                                    |
| ------- | --------------- | ------- | -------------------------------------------------------------- |
| `error` | `Error \| null` |         | The error to show. Without one, the indicator renders nothing. |

## LoadingIndicatorProps

`LoadingIndicatorProps` are the props of an SVG element, plus:

| Prop         | Type      | Default | Description                                                                    |
| ------------ | --------- | ------- | ------------------------------------------------------------------------------ |
| `decorative` | `boolean` | `true`  | Hides the spinner from assistive technology and keeps it out of the tab order. |

It passes them to the `IconLoading` icon, with the class `str-chat__loading-indicator` in place of any `className` you pass.

## LoadMoreButtonProps

| Prop        | Type                                         | Default       | Description                                                   |
| ----------- | -------------------------------------------- | ------------- | ------------------------------------------------------------- |
| `onClick`   | `React.MouseEventHandler<HTMLButtonElement>` | required      | Loads the next page.                                          |
| `isLoading` | `boolean`                                    |               | Disables the button and shows a spinner instead of its label. |
| `children`  | `ReactNode`                                  | `"Load more"` | The label.                                                    |

## LoadMorePaginatorProps

| Prop             | Type                                       | Default          | Description                                                |
| ---------------- | ------------------------------------------ | ---------------- | ---------------------------------------------------------- |
| `loadNextPage`   | `() => void`                               | required         | Called by the button.                                      |
| `hasNextPage`    | `boolean`                                  |                  | Shows the button while `true`.                             |
| `isLoading`      | `boolean`                                  |                  | Passed to the button.                                      |
| `LoadMoreButton` | `React.ComponentType<LoadMoreButtonProps>` | `LoadMoreButton` | The button to render.                                      |
| `reverse`        | `boolean`                                  |                  | Puts the button before the children instead of after them. |
| `children`       | `ReactNode`                                |                  | The list.                                                  |

`LoadMorePaginatorProps` also accepts `hasPreviousPage`, `loadPreviousPage`, `LoadingIndicator` and `threshold`, from the shared `PaginatorProps` type, but `LoadMorePaginator` doesn't read them.

## TypingIndicatorProps

| Prop                            | Type         | Default | Description                                                                 |
| ------------------------------- | ------------ | ------- | --------------------------------------------------------------------------- |
| `isMessageListScrolledToBottom` | `boolean`    | `true`  | When `false`, the indicator renders nothing.                                |
| `scrollToBottom`                | `() => void` |         | Called when the indicator appears while the list is scrolled to the bottom. |

Whether it shows typing in the channel or in a thread depends on whether it renders inside a `Thread`, not on a prop.

## TypingIndicatorHeaderProps

| Prop         | Type      | Default | Description                                                                        |
| ------------ | --------- | ------- | ---------------------------------------------------------------------------------- |
| `threadList` | `boolean` | `false` | Shows typing in the replies of the surrounding `Thread` instead of in the channel. |

## Examples

### Replace the message list's empty state

```tsx
import { Channel, MessageList, WithComponents } from "stream-chat-react";
import type { EmptyStateIndicatorProps } from "stream-chat-react";

const EmptyChannel = ({ listType }: EmptyStateIndicatorProps) =>
  listType === "message" ? (
    <p className="app-empty-channel">No messages yet. Say hi!</p>
  ) : null;

export const App = () => (
  <WithComponents overrides={{ EmptyStateIndicator: EmptyChannel }}>
    <Channel channel={channel}>
      <MessageList />
    </Channel>
  </WithComponents>
);
```

Returning `null` for `"thread"` keeps a thread's list empty, as the default does.

### Replace the thread list's load error

```tsx
import { ThreadList, WithComponents } from "stream-chat-react";
import type { ListLoadErrorIndicatorProps } from "stream-chat-react";

const ThreadsLoadError = ({
  isRetrying,
  retry,
}: ListLoadErrorIndicatorProps) => (
  <div className="app-threads-error">
    <p>Your threads didn't load.</p>
    <button
      aria-busy={isRetrying}
      aria-disabled={isRetrying}
      onClick={retry}
      type="button"
    >
      Try again
    </button>
  </div>
);

const overrides = { ListLoadErrorIndicator: ThreadsLoadError };

export const Threads = () => (
  <WithComponents overrides={overrides}>
    <ThreadList />
  </WithComponents>
);
```

The override wraps `<ThreadList/>` only, so the channel list keeps its default error view.

### Replace the channel list's empty state

```tsx
import { ChannelList, WithComponents } from "stream-chat-react";

const NoChannels = () => (
  <p className="app-no-channels">Join a channel to start.</p>
);

export const Channels = () => (
  <WithComponents overrides={{ EmptyListIndicator: NoChannels }}>
    <ChannelList paginator={paginator} />
  </WithComponents>
);
```

### Show typing in VirtualizedMessageList

```tsx
import {
  Channel,
  TypingIndicator,
  VirtualizedMessageList,
  WithComponents,
} from "stream-chat-react";

export const App = () => (
  <WithComponents overrides={{ TypingIndicator }}>
    <Channel channel={channel}>
      <VirtualizedMessageList />
    </Channel>
  </WithComponents>
);
```

---

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