Skip to main content
Version: v6

Building a Conversation List + Message View

The Conversation List + Message View layout offers a seamless two-panel chat interface, commonly used in modern messaging applications like WhatsApp Web, Slack, and Microsoft Teams.

This design enables users to switch between conversations effortlessly while keeping the chat window open, ensuring a smooth, real-time messaging experience.


User Interface Overview

Image

This layout is structured into three key sections:

  1. Sidebar (Conversation List) – Displays active conversations, including users and groups.
  2. Message View – Shows chat messages for the selected conversation in real-time.
  3. Message Composer – Provides an input field for typing and sending messages, along with support for media, emojis, and reactions.

Step-by-Step Guide

Step 1: Create Sidebar

Let's create the Sidebar component which will render different conversations.

Folder Structure

Create a CometChatSelector folder inside your src directory and add the following files:

src/
│── CometChatSelector/
│ ├── assets/
│ │ ├── chats.svg # This is the image you need to save
│ ├── CometChatSelector.tsx
│ ├── CometChatSelector.css

Download the Icon

These icons are available in the CometChat UI Kit assets folder. You can find them at:
🔗 GitHub Assets Folder

CometChatSelector.tsx
import { useEffect, useState } from "react";
import {
Conversation,
Group,
User,
CometChat,
} from "@cometchat/chat-sdk-javascript";
import {
CometChatConversations,
CometChatUIKitLoginListener,
} from "@cometchat/chat-uikit-react";
import "./CometChatSelector.css";

// Define the props for the CometChatSelector component
interface SelectorProps {
onSelectorItemClicked?: (
input: User | Group | Conversation,
type: string
) => void;
onHide?: () => void;
onNewChatClicked?: () => void;
}

// CometChatSelector component
export const CometChatSelector = (props: SelectorProps) => {
// Destructure props with a default function for onSelectorItemClicked
const { onSelectorItemClicked = () => {} } = props;

// State to store the logged-in user
const [loggedInUser, setLoggedInUser] = useState<CometChat.User | null>();

// State to track the currently selected item (it can be a Conversation, User, or Group)
const [activeItem, setActiveItem] = useState<
CometChat.Conversation | CometChat.User | CometChat.Group | undefined
>();

// useEffect hook to fetch and set the logged-in user
useEffect(() => {
const loggedInUser = CometChatUIKitLoginListener.getLoggedInUser();
setLoggedInUser(loggedInUser);
}, [loggedInUser]); // Dependency on loggedInUser causes unnecessary re-renders

return (
<>
{/* Render chat conversations if a user is logged in */}
{loggedInUser && (
<>
<CometChatConversations
activeConversation={
activeItem instanceof CometChat.Conversation
? activeItem
: undefined
}
onItemClick={(e) => {
setActiveItem(e); // Update the active item when an item is clicked
onSelectorItemClicked(e, "updateSelectedItem"); // Notify parent component
}}
/>
</>
)}

{/* Bottom navigation for chat selection */}
<div className="cometchat-selector">
{
<div key={"chats"} className="cometchat-selector__tab">
<div className="cometchat-selector__tab-icon-active" />
<div className="cometchat-selector__tab-text-active">CHATS</div>
</div>
}
</div>
</>
);
};

Step 2: Update App

Now we will update the App.tsx & App.css files to import these new components as below,

App.tsx
import { useState } from "react";
import {
CometChatMessageComposer,
CometChatMessageHeader,
CometChatMessageList,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatSelector } from "./CometChatSelector/CometChatSelector";
import "./App.css";

function App() {
// State to track the currently selected user
const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>(undefined);

// State to track the currently selected group
const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>(undefined);

return (
<div className="conversations-with-messages">
{/* Sidebar for selecting conversations */}
<div className="conversations-wrapper">
<CometChatSelector
onSelectorItemClicked={(activeItem) => {
let item = activeItem;

// If the selected item is a conversation, extract the user/group from it
if (activeItem instanceof CometChat.Conversation) {
item = activeItem.getConversationWith();
}

// Determine if the selected item is a User or a Group and update the state accordingly
if (item instanceof CometChat.User) {
setSelectedUser(item as CometChat.User);
setSelectedGroup(undefined); // Ensure no group is selected
} else if (item instanceof CometChat.Group) {
setSelectedUser(undefined); // Ensure no user is selected
setSelectedGroup(item as CometChat.Group);
} else {
setSelectedUser(undefined);
setSelectedGroup(undefined); // Reset if selection is invalid
}
}}
/>
</div>

{/* If a user or group is selected, display the chat interface */}
{selectedUser || selectedGroup ? (
<div className="messages-wrapper">
{/* Header displaying user/group details */}
<CometChatMessageHeader user={selectedUser} group={selectedGroup} />

{/* List of messages for the selected user/group */}
<CometChatMessageList user={selectedUser} group={selectedGroup} />

{/* Message input composer */}
<CometChatMessageComposer user={selectedUser} group={selectedGroup} />
</div>
) : (
// Default message when no conversation is selected
<div className="empty-conversation">Select Conversation to start</div>
)}
</div>
);
};

export default App;

Step 3: Run the project

npm start

Next Steps

Enhance the User Experience