# Custom Thread List

This cookbook shows how to build a custom screen with [`ThreadList`](/chat/docs/sdk/react-native/v8/ui-components/thread-list/) and a banner for unread threads.

## Best Practices

- Keep `ThreadList` within `Chat` so it has access to client state and contexts.
- Use `isFocused` to avoid unnecessary updates when the screen is not visible.
- Reuse `onThreadSelect` so thread navigation stays consistent across the app.
- Keep custom `ThreadListItem` lightweight to maintain scroll performance.
- Pull unread counts from the state store to avoid extra client queries.

### Prerequisites

Prereqs: a screen that shows a [`Thread`](/chat/docs/sdk/react-native/v8/ui-components/thread/) and a working `chatClient`. Examples use React Navigation, but any navigation library works.

### Creating the screen

Add [`ThreadList`](/chat/docs/sdk/react-native/v8/ui-components/thread-list/) to a new screen:

```tsx
import { OverlayProvider, Chat, ThreadList } from "stream-chat-react-native";

const ThreadListScreen = () => {
  return (
    <OverlayProvider>
      <Chat client={client}>
        <ThreadList />
      </Chat>
    </OverlayProvider>
  );
};
```

This renders the user's threads with the default UI.

![Step 1 Preview](@chat-sdk/react-native/v8/_assets/guides/custom-thread-list/custom-thread-list-step-1.png)

Next, update the list only when the screen is focused using `isFocused`:

```tsx {2,3,6,10}
import { OverlayProvider, Chat, ThreadList } from "stream-chat-react-native";
// any navigation library hook/method can be used for this
import { useIsFocused } from "@react-navigation/native";

const ThreadListScreen = () => {
  const isFocused = useIsFocused();
  return (
    <OverlayProvider>
      <Chat client={client}>
        <ThreadList isFocused={isFocused} />
      </Chat>
    </OverlayProvider>
  );
};
```

This is useful when `ThreadList` lives in a tab that stays mounted.

Now the list refreshes only when focused. Next, handle item taps and navigate to the thread:

```tsx {3,7,13,14,15,16,17,18,19}
import { OverlayProvider, Chat, ThreadList } from "stream-chat-react-native";
// any navigation library hook/method can be used for this
import { useNavigation, useIsFocused } from "@react-navigation/native";

const ThreadListScreen = () => {
  const isFocused = useIsFocused();
  const navigation = useNavigation();
  return (
    <OverlayProvider>
      <Chat client={client}>
        <ThreadList
          isFocused={isFocused}
          // here we can reuse the same method as we would in the ChannelList component
          onThreadSelect={(thread, channel) => {
            navigation.navigate("ThreadScreen", {
              thread,
              channel,
            });
          }}
        />
      </Chat>
    </OverlayProvider>
  );
};
```

Now you can navigate to any thread. Next, render only the thread ID per item.

Override `ThreadListItem`:

```tsx {2,3,4,5,6,7,8,9,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,50}
import { TouchableOpacity, Text } from 'react-native';
import type { LocalMessage } from 'stream-chat';
import {
  OverlayProvider,
  Chat,
  ThreadList,
  useThreadsContext,
  useThreadListItemContext,
} from 'stream-chat-react-native';

// any navigation library hook/method can be used for this
import { useNavigation, useIsFocused } from '@react-navigation/native';

const ThreadListItem = () => {
  // we grab the definition of the navigation function from the ThreadsContext
  const { onThreadSelect } = useThreadsContext();
  // we grab the actual thread, channel and its parent message from the ThreadListItemContext
  const { channel, thread, parentMessage } = useThreadListItemContext();
  return (
    <TouchableOpacity
      style={{ backgroundColor: 'red', padding: 5 }}
      onPress={() => {
        if (onThreadSelect) {
          // since we are overriding the behaviour of the item it is mandatory to pass the parameters in the
          // below to onThreadSelect()
          onThreadSelect({ thread: parentMessage as LocalMessage, threadInstance: thread }, channel);
        }
      }}
     >
      <Text>{thread?.id}</Text>
    </TouchableOpacity>
  )
}

const ThreadListScreen = () => {
  const isFocused = useIsFocused();
  const navigation = useNavigation();
  return (
    <OverlayProvider>
      <Chat client={client}>
        <ThreadList
          isFocused={isFocused}
          {/* here we can reuse the same method as we would in the ChannelList component */}
          onThreadSelect={(thread, channel) => {
            navigation.navigate('ThreadScreen', {
              thread,
              channel,
            });
          }}
          ThreadListItem={ThreadListItem}
        />
      </Chat>
    </OverlayProvider>
  );
};
```

Now your custom items should render.

![Step 2 Preview](@chat-sdk/react-native/v8/_assets/guides/custom-thread-list/custom-thread-list-step-2.png)

Next, add a banner above the list with the unread thread count.

Use the [state store](/chat/docs/sdk/react-native/v8/state-and-offline-support/state-overview#thread-and-threadmanager/) and [`useStateStore`](/chat/docs/sdk/react-native/v8/state-and-offline-support/state-overview#usestatestore-hook/):

```tsx {9,11,17,18,19,20,21,22,23,24,25,26,27,28,29,30,38,39}
import { TouchableOpacity, Text, View } from 'react-native';
import {
  OverlayProvider,
  Chat,
  ThreadList,
  useThreadsContext,
  useThreadListItemContext,
  useStateStore,
} from 'stream-chat-react-native';
import type { LocalMessage } from 'stream-chat';
import { ThreadManagerState } from 'stream-chat';
// any navigation library hook/method can be used for this
import { useNavigation, useIsFocused } from '@react-navigation/native';

// ...

// create a selector for unreadThreadCount
const selector = (nextValue: ThreadManagerState) => [nextValue.unreadThreadCount];

const CustomBanner = () => {
  // use our utility hook to access the store
  const [unreadCount] = useStateStore(client?.threads?.state, selector);

  // display the banner
  return (
     <View style={{ paddingVertical: 15, paddingHorizontal: 5 }}>
       <Text>You have {unreadCount} unread threads !</Text>
     </View>
  );
};

const ThreadListScreen = () => {
  const isFocused = useIsFocused();
  const navigation = useNavigation();
  return (
    <OverlayProvider>
      <Chat client={client}>
        {/* it's important that the banner is also a child of <Chat /> */}
        <CustomBanner />
        <ThreadList
          isFocused={isFocused}
          {/* here we can reuse the same method as we would in the ChannelList component */}
          onThreadSelect={(thread, channel) => {
            navigation.navigate('ThreadScreen', {
              thread,
              channel,
            });
          }}
          ThreadListItem={ThreadListItem}
        />
      </Chat>
    </OverlayProvider>
  );
};
```

![Step 3 Preview](@chat-sdk/react-native/v8/_assets/guides/custom-thread-list/custom-thread-list-step-3.png)

Finally, add 10px spacing using `FlatList` props via `additionalFlatListProps`.

Final example:

```tsx {3,23,24,25}
// ...

const ItemSeparatorComponent = () => <View style={{ paddingVertical: 5 }} />

const ThreadListScreen = () => {
  const isFocused = useIsFocused();
  const navigation = useNavigation();
  return (
    <OverlayProvider>
      <Chat client={client}>
        {/* it's important that the banner is also a child of <Chat /> */}
        <CustomBanner />
        <ThreadList
          isFocused={isFocused}
          {/* here we can reuse the same method as we would in the ChannelList component */}
          onThreadSelect={(thread, channel) => {
            navigation.navigate('ThreadScreen', {
              thread,
              channel,
            });
          }}
          ThreadListItem={ThreadListItem}
          additionalFlatListProps={{
            ItemSeparatorComponent,
          }}
        />
      </Chat>
    </OverlayProvider>
  );
};
```

![Step 4 Preview](@chat-sdk/react-native/v8/_assets/guides/custom-thread-list/custom-thread-list-step-4.png)

You now have a fully customized `ThreadList`.


---

This page was last updated at 2026-04-17T17:33:45.290Z.

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