Skip to main content

Channel List and Preview

How-to Guide for Customizing the ChannelList UI#

To customize the UI for the ChannelList 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 and ChannelPreviewMessenger.

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.

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

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

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.

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.

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 of the props, and incorporating other logic that fit your requirements. Check out the CSS section for a note on custom styling.

The Final Code:

const CustomList = (props: PropsWithChildren<ChannelListMessengerProps>) => {  const { children, error, loading, LoadingErrorIndicator, LoadingIndicator } = props;
  if (error) {    return <LoadingErrorIndicator 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 text={'Loading Error - check your connection.'} type={'connection'} />    )}    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 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.

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 for styling. We'll import the bundled CSS into the file where the application is instantiated then apply a class to the button element.

import '@stream-io/stream-chat-css/dist/css/index.css';
<div  className={'str-chat__channel-preview-messenger'}  onClick={() => setActiveChannel(channel, watchers)}>

Next, let's add a little 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 section.

The Final Code:

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>  );};
.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;}
<Chat client={client}>  <ChannelList Preview={CustomPreview} showChannelSearch />  <Channel>    <Window>      <ChannelHeader />      <MessageList />      <MessageInput />    </Window>    <Thread />  </Channel></Chat>

Result:

Custom Preview UI Component for Chat

Did you find this page helpful?