# Thread list UI

The thread list is the screen of threads the user takes part in. Each thread list item shows one thread's channel, parent message and latest reply. Use this page to know what `ThreadList` loads and renders, what each thread list item shows and reads, and which parts you can replace. An open thread is on [Thread UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/thread-ui/). The props are in the [ThreadList](https://getstream.io/chat/docs/sdk/react-native/component-reference/thread-list/threadlist/) and [ThreadListItem](https://getstream.io/chat/docs/sdk/react-native/component-reference/thread-list/threadlistitem/) references.

## The ThreadList component

`<ThreadList isFocused={isFocused} onThreadSelect={onThreadSelect} />` renders the threads of `client.threads`, the thread manager. What it does, and when:

| When                                      | `ThreadList`                                                                                                                                                          |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isFocused` is `true`                     | Activates `client.threads`: the first page loads when the list has none, and the list reloads when threads got replies while it was inactive. It's `true` by default. |
| `isFocused` turns `false`, or it unmounts | Deactivates `client.threads`                                                                                                                                          |
| `client.threads.paginator.state` changes  | Renders a thread list item for each thread, in a React Native `FlatList`, in the server's order: unread threads first, then by latest reply                           |
| The user scrolls to the end               | Loads the next page                                                                                                                                                   |
| The user taps an item                     | Calls `onThreadSelect(thread)`, and your app shows the thread. Without `onThreadSelect`, the tap does nothing.                                                        |

Pass the focus state of the list's screen as `isFocused`, such as `useIsFocused()` from React Navigation, so the list stays active only while the user can see it. Below the items, the list renders a `NotificationList` for the notifications published to the thread list; see [Notifications UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/).

## Thread list items

Each thread list item reads its thread's stores and provides what it reads to the components inside it:

| The item shows                                                                                                                                           | Read from                                                                                  |
| -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| The avatar of the latest reply's author                                                                                                                  | `thread.messagePaginator.aggregateState`: `latestMessage`                                  |
| The channel's name                                                                                                                                       | `thread.state`: `channel`                                                                  |
| The parent message, or "Draft:" and the draft when one was left                                                                                          | `thread.state`: `parentMessage`, and the thread's composer: its text and attachments       |
| The sender before the parent message: "You:" with a delivery mark for the user's own, the sender's name for another member's, except in a direct message | `channel.messageReceiptsTracker.snapshotStore`, and the channel's read events setting      |
| Up to 3 participants' avatars                                                                                                                            | `parentMessage.thread_participants`                                                        |
| The reply count                                                                                                                                          | `parentMessage.reply_count`                                                                |
| The time of the latest reply, or the time the parent was deleted                                                                                         | `thread.messagePaginator.aggregateState`: `latestMessage`, and `thread.state`: `deletedAt` |
| The unread count, when there is one                                                                                                                      | `thread.state`: `read[userId].unreadMessageCount`. Not shown for a deleted parent message. |

`ThreadListItem` provides these to the components inside it, which read them with `useThreadListItemContext()`: `thread`, `channel`, `parentMessage`, `lastReply`, `ownUnreadMessageCount`, `draftMessage`, `dateString` and `deletedAtDateString`. The default item renders the `ThreadListItem` key with no props.

### The unread banner

A thread that isn't in the loaded list can get a reply, for example an older thread someone answers. The thread manager records it in `client.threads.state` as unseen, and the list shows a banner above the items: "2 new threads". Tapping it reloads the list, which brings those threads in at their new position. While it reloads, the banner reads "Loading...". If the reload fails, the banner reads "Couldn't load new threads. Tap to retry", and tapping it tries again.

## Loading, empty and error states

`ThreadList` shows a state of its own while the first page loads, while the next page loads, and when it has no threads. It has no error view of its own for the first page: a query that fails leaves the list as it was, or the empty placeholder when nothing was loaded, and the error is recorded as `lastQueryError` in `client.threads.paginator.state`. [List states UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/list-states-ui/#the-thread-list) lists each state's default and override key.

## Replacing parts

Register replacements with `WithComponents`. [Overriding components](https://getstream.io/chat/docs/sdk/react-native/ui/components/overriding-components/) explains how overrides apply.

| Override key                                                                                 | Replaces                                                                                                  |
| -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `ThreadListComponent`                                                                        | The whole thread list: the banner and the items. Reads `useThreadsContext()`.                             |
| `ThreadListItem`                                                                             | An item. Gets no props; reads `useThreadListItemContext()`.                                               |
| `ThreadListItemMessagePreview`                                                               | The parent message or draft in an item                                                                    |
| `ThreadMessagePreviewDeliveryStatus`                                                         | The sender and delivery mark before the parent message in an item                                         |
| `ThreadListUnreadBanner`                                                                     | The unread banner                                                                                         |
| `ThreadListLoadingIndicator`, `ThreadListLoadingMoreIndicator`, `ThreadListEmptyPlaceholder` | The [loading and empty states](https://getstream.io/chat/docs/sdk/react-native/ui/components/list-states-ui/#the-thread-list) |

The default item, `ThreadListItemComponent`, isn't exported, so a replacement for it can't render the default inside it.

This thread list renders compact items of its own, with the channel's name, the parent message's text and the unread count:

```tsx title="src/ThreadsScreen.tsx"
import { Pressable, StyleSheet, Text, View } from "react-native";
import { useIsFocused } from "@react-navigation/native";
import type { Thread as StreamThread } from "stream-chat";
import {
  ThreadList,
  useChannelPreviewDisplayName,
  useThreadListItemContext,
  useThreadsContext,
  WithComponents,
} from "stream-chat-react-native";

const CompactThreadRow = () => {
  const { channel, ownUnreadMessageCount, parentMessage, thread } =
    useThreadListItemContext();
  const { onThreadSelect } = useThreadsContext();
  const channelName = useChannelPreviewDisplayName(channel);

  return (
    <Pressable onPress={() => onThreadSelect?.(thread)} style={styles.row}>
      <View style={styles.text}>
        <Text numberOfLines={1} style={styles.channel}>
          {channelName}
        </Text>
        <Text numberOfLines={1}>{parentMessage?.text}</Text>
      </View>
      {ownUnreadMessageCount > 0 ? <Text>{ownUnreadMessageCount}</Text> : null}
    </Pressable>
  );
};

const overrides = { ThreadListItem: CompactThreadRow };

export const ThreadsScreen = ({
  onThreadSelect,
}: {
  onThreadSelect: (thread: StreamThread) => void;
}) => {
  const isFocused = useIsFocused();

  return (
    <WithComponents overrides={overrides}>
      <ThreadList isFocused={isFocused} onThreadSelect={onThreadSelect} />
    </WithComponents>
  );
};

const styles = StyleSheet.create({
  channel: { fontWeight: "600" },
  row: { alignItems: "center", flexDirection: "row", gap: 8, padding: 12 },
  text: { flex: 1 },
});
```

The item reads the parent message and the unread count from the item's context, which follows `thread.state`, so it updates when a reply arrives. The [Custom thread list UI cookbook](https://getstream.io/chat/docs/sdk/react-native/ui-cookbooks/screens-and-sheets/threads-view/) builds a thread list of its own.

## Used in

- [Replies and threads](https://getstream.io/chat/docs/sdk/react-native/features/messaging/replies-and-threads/)
- [Threads view](https://getstream.io/chat/docs/sdk/react-native/ui-cookbooks/screens-and-sheets/threads-view/)

---

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