---
id: channel_list_preview
sidebar_position: 3
title: Channel List and Preview
---

import CustomPreview from '../../assets/CustomPreview.png';

## How-to Guide for Customizing the ChannelList UI

To customize the UI for the [`ChannelList`](../../components/core-components/channel-list.mdx) you need to focus on the two components that dictate the UI, the list and preview components. These are set via the optional `List` and `Preview` props, and if nothing is provided they default to
[`ChannelListMessenger`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx)
and [`ChannelPreviewMessenger`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelPreview/ChannelPreviewMessenger.tsx).

Here is a guide to build these two UI components to override the defaults.

## The List Prop Component

The list component used by `ChannelList` receives the same props as the default and is a container that renders the list of channels via the children prop. The return block in the code below is an example of the minimum necessary elements
needed for a custom list component.

```jsx
return <div>{props.children}</div>;
```

Using the default `ChannelListMessenger` component as a model, let's incorporate the other props to render the list conditionally.

```jsx
if (props.error) {
  return <div>Warning! Error Message!</div>;
}

if (props.loading) {
  return null;
}
```

The items we're rendering in those conditionals are not very helpful for the user, so let's take advantage of several of the library's status indicator components via the `LoadingErrorIndicator` and `LoadingIndicator`
props and assign the default values for each, `ChatDown` and `LoadingChannels`. This assignment is necessary here to satisfy TypeScript since these props are optional.

```jsx
import { ChatDown, LoadingChannels } from 'stream-chat-react';

const { LoadingErrorIndicator = ChatDown, LoadingIndicator = LoadingChannels } = props;

if (error) {
  return <LoadingErrorIndicator type={'connection'} />;
}

if (loading) {
  return <LoadingIndicator />;
}
```

To use a custom component (or a different library component) for either the `LoadingErrorIndicator` or `LoadingIndicator` props here (and in the default list component, too),
you can utilize the props of the same names on `ChannelList`. If either of these props are provided, then the assignment mentioned in the above step is not necessary.

```jsx
const CustomErrorIndicator = (props: ChatDownProps) => {
  return <div>So much error when rendering.</div>;
};

<ChannelList
  LoadingErrorIndicator={(props) => <CustomErrorIndicator {...props} type={'connection'} />}
/>;
```

This is a basic example, but possibilities are endless for
adding additional UI items, not using all the props, and incorporating other logic that fit your requirements.

**The Final Code:**

```jsx
const CustomList = (props: PropsWithChildren<ChannelListMessengerProps>) => {
  const { children, error, loading, LoadingErrorIndicator, LoadingIndicator } = props;

  if (error) {
    return <LoadingErrorIndicator text='Loading Error - check your connection.' type='connection' />;
  }

  if (loading) {
    return <LoadingIndicator />;
  }

  return <div>{children}</div>;
};

const CustomErrorIndicator = (props: ChatDownProps) => {
  const { text } = props;

  return <div>{text}</div>;
};

const CustomLoadingIndicator = () => {
  return <div>Loading, loading, loading...</div>;
};

<Chat client={client}>
  <ChannelList
    List={CustomList}
    LoadingErrorIndicator={CustomErrorIndicator}
    LoadingIndicator={CustomLoadingIndicator}
  />
  <Channel>
    <MessageList />
    <MessageInput />
  </Channel>
</Chat>;
```

## The Preview Prop Component

The component set via the `Preview` prop (or the default) dictates the design and click functionality of an individual channel in the list that we built above.
Ultimately, these UI components are rendered via the [`ChannelPreview`](../../components/utility-components/channel-preview.mdx) wrapper component.

Here is a guide to build a custom UI component to override the default using the large variety of props provided.
The code snippet below includes the bare minimum code that would be required for standard preview functionality (click handler and active channel selection), and anything additional would be to satisfy unique requirements.

```tsx
import { ChannelPreviewUIComponentProps } from 'stream-chat-react';

export const CustomPreview = (props: ChannelPreviewUIComponentProps) => {
  const { channel, setActiveChannel, watchers } = props;

  return (
    <div onClick={() => setActiveChannel(channel, watchers)}>
      <span>{channel.data.name || 'Channel Preview'}</span>
    </div>
  );
};
```

Let's take advantage of the SDK's [CSS classes](../theming/css-and-theming.mdx) for styling. We'll import the bundled CSS into the file where the application is instantiated then apply a class to the button element.

```tsx
import 'stream-chat-react/dist/css/v2/index.css';

<div
  className={'str-chat__channel-preview-messenger'}
  onClick={() => setActiveChannel(channel, watchers)}
>
```

Next, let's add a bit more useful information to the component using more of the default props and a value pulled from the `ChatContext`, as well as some styling using custom CSS.
This context also exposes the client which makes it possible to use API methods.

:::note
For a complete overview of the available props in the custom component, take a look at the [ChannelPreview UI props](../../components/utility-components/channel-preview-ui.mdx#props) section.
:::

**The Final Code:**

```tsx
import { ChannelPreviewUIComponentProps, useChatContext } from 'stream-chat-react';
import './CustomPreview.scss';

export const CustomPreview = (props: ChannelPreviewUIComponentProps) => {
  const { channel, setActiveChannel } = props;

  const { channel: activeChannel } = useChatContext();

  const selected = channel?.id === activeChannel?.id;

  const renderMessageText = () => {
    const lastMessageText = channel.state.messages[channel.state.messages.length - 1].text;

    const text = lastMessageText || 'message text';

    return text.length < 60 ? lastMessageText : `${text.slice(0, 70)}...`;
  };

  if (!channel.state.messages.length) return null;

  return (
    <div
      className={selected ? 'channel-preview__container selected' : 'channel-preview__container'}
      onClick={() => setActiveChannel(channel)}
    >
      <div className='channel-preview__content-wrapper'>
        <div className='channel-preview__content-top'>
          <p className='channel-preview__content-name'>{channel.data?.name || 'Channel'}</p>
          <p className='channel-preview__content-name'>{channel.data?.subtitle}</p>
        </div>
        <p className='channel-preview__content-message'>{renderMessageText()}</p>
      </div>
    </div>
  );
};
```

```css
.channel-preview__container {
  height: 56px;
  margin-bottom: 8px;
  margin-left: 20px;
  margin-right: 20px;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-left: 8px;
}

.channel-preview__container:hover {
  background: #ffffff;
  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.07);
  transition: background 0.1s ease-in-out;
}

.channel-preview__container.selected {
  background: #ffffff;
  box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.07);
  transition: background 0.1s ease-in-out;
}

.channel-preview__content-wrapper {
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin-left: 8px;
  margin-right: 8px;
  width: 100%;
}

.channel-preview__content-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0;
  height: 18px;
  margin-bottom: 4px;
}

.channel-preview__content-name {
  font-family: Helvetica Neue, sans-serif;
  font-weight: 500;
  font-size: 15px;
  color: #000000;
  margin: 0;
  max-width: 158px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.channel-preview__content-message {
  font-family: Helvetica Neue, sans-serif;
  font-size: 13px;
  line-height: 16px;
  margin: 0;
  color: #858688;
  height: 16px;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
```

```tsx
<Chat client={client}>
  <ChannelList Preview={CustomPreview} showChannelSearch />
  <Channel>
    <Window>
      <ChannelHeader />
      <MessageList />
      <MessageInput />
    </Window>
    <Thread />
  </Channel>
</Chat>
```

**Result:**

<img src={CustomPreview} alt='Custom Preview UI Component for Chat' width='500' />
