Building a Messaging UI with Tabs, Sidebar, and Message View
This guide walks you through creating a tab-based messaging UI using React and CometChat UIKit. The UI will include different sections for Chats, Calls, Users, and Groups, allowing seamless navigation.
Tip: You can fork the sandbox, insert your CometChat credentials (App ID, Region, Auth Key.) in the code, and immediately preview how the UI and messages respond in real time.
User Interface Preview

This layout consists of:
- Sidebar (Conversation List) – Displays recent conversations with active users and groups.
- Message View – Shows the selected chat with real-time messages.
- Message Input Box – Allows users to send messages seamlessly.
Step-by-Step Guide
Step 1: Create a Tab Component
To manage navigation, let's build a CometChatTabs
component. This component will render different tabs and allow switching between sections dynamically.
Folder Structure
Create a CometChatTabs
folder inside your src
directory and add the following files:
src/
│── CometChatTabs/
│ ├── assets # These are the images you need to save
│ │ ├── chats.svg
│ │ ├── calls.svg
│ │ ├── users.svg
│ │ ├── groups.svg
│ ├── CometChatTabs.tsx
│ ├── CometChatTabs.css
Download the Icons
These icons are available in the CometChat UI Kit assets folder. You can find them at:
🔗 GitHub Assets Folder
Implementation
- TypeScript
- CSS
import { useState } from "react";
import chatsIcon from "./assets/chats.svg";
import callsIcon from "./assets/calls.svg";
import usersIcon from "./assets/users.svg";
import groupsIcon from "./assets/groups.svg";
import "./CometChatTabs.css";
export const CometChatTabs = (props: {
onTabClicked?: (tabItem: { name: string; icon?: string; }) => void;
activeTab?: string;
}) => {
const {
onTabClicked = () => { },
activeTab
} = props;
const [hoverTab, setHoverTab] = useState("");
const tabItems = [
{
"name": "CHATS",
"icon": chatsIcon
},
{
"name": "CALLS",
"icon": callsIcon
},
{
"name": "USERS",
"icon": usersIcon
},
{
"name": "GROUPS",
"icon": groupsIcon
}
]
return (
<div className="cometchat-tab-component">
{
tabItems.map((tabItem) => (
<div
key={tabItem.name}
className="cometchat-tab-component__tab"
onClick={() => onTabClicked(tabItem)}
>
<div
className={(activeTab === tabItem.name.toLowerCase() || hoverTab === tabItem.name.toLowerCase()) ? "cometchat-tab-component__tab-icon cometchat-tab-component__tab-icon-active" : "cometchat-tab-component__tab-icon"}
style={tabItem.icon ? {
WebkitMaskImage: `url("${tabItem.icon}")`,
maskImage: `url("${tabItem.icon}")`,
} : undefined}
onMouseEnter={() => setHoverTab(tabItem.name.toLowerCase())}
onMouseLeave={() => setHoverTab("")}
/>
<div
className={(activeTab === tabItem.name.toLowerCase() || hoverTab === tabItem.name.toLowerCase()) ? "cometchat-tab-component__tab-text cometchat-tab-component__tab-text-active" : "cometchat-tab-component__tab-text"}
onMouseEnter={() => setHoverTab(tabItem.name.toLowerCase())}
onMouseLeave={() => setHoverTab("")}
>
{tabItem.name}
</div>
</div>
))
}
</div>
)
}
.cometchat-tab-component {
display: flex;
width: 100%;
padding: 0px 8px;
align-items: flex-start;
gap: 8px;
border-top: 1px solid var(--cometchat-border-color-light, #F5F5F5);
border-right: 1px solid var(--cometchat-border-color-light, #F5F5F5);
background: var(--cometchat-background-color-01, #FFF);
}
.cometchat-tab-component__tab {
display: flex;
padding: 12px 0px 10px 0px;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4px;
flex: 1 0 0;
min-height: 48px;
}
.cometchat-tab-component__tab-icon {
display: flex;
width: 32px;
height: 32px;
justify-content: center;
align-items: center;
background: var(--cometchat-icon-color-secondary, #A1A1A1);
-webkit-mask-size: contain;
-webkit-mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
mask-repeat: no-repeat;
cursor: pointer;
}
.cometchat-tab-component__tab-text {
color: var(--cometchat-text-color-secondary, #727272);
text-align: center;
font: var(--cometchat-font-caption1-medium, 500 12px Roboto);
cursor: pointer;
}
.cometchat-tab-component__tab-icon-active {
background: var(--cometchat-icon-color-highlight);
}
.cometchat-tab-component__tab-text-active {
color: var(--cometchat-text-color-highlight);
}
Step 2: 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/
│ ├── CometChatSelector.tsx
│ ├── CometChatSelector.css
- TypeScript
- CSS
import { useEffect, useState } from "react";
import { Call, Conversation, Group, User, CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatCallLogs, CometChatConversations, CometChatGroups, CometChatUIKitLoginListener, CometChatUsers } from "@cometchat/chat-uikit-react";
import { CometChatTabs } from "../CometChatTabs/CometChatTabs";
import "./CometChatSelector.css";
interface SelectorProps {
onSelectorItemClicked?: (input: User | Group | Conversation | Call, type: string) => void;
onHide?: () => void;
onNewChatClicked?: () => void;
}
export const CometChatSelector = (props: SelectorProps) => {
const {
onSelectorItemClicked = () => { },
} = props;
const [loggedInUser, setLoggedInUser] = useState<CometChat.User | null>();
const [activeItem, setActiveItem] = useState<CometChat.Conversation | CometChat.User | CometChat.Group | CometChat.Call | undefined>();
const [activeTab, setActiveTab] = useState<string>("chats");
useEffect(() => {
const loggedInUsers = CometChatUIKitLoginListener.getLoggedInUser();
setLoggedInUser(loggedInUsers)
}, [])
return (
<>
{loggedInUser && <>
{activeTab == "chats" ? (
<CometChatConversations
activeConversation={activeItem instanceof CometChat.Conversation ? activeItem : undefined}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItem");
}}
/>
) : activeTab == "calls" ? (
<CometChatCallLogs
activeCall={activeItem instanceof CometChat.Call ? activeItem : undefined}
onItemClick={(e: Call) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemCall");
}}
/>
) : activeTab == "users" ? (
<CometChatUsers
activeUser={activeItem instanceof CometChat.User ? activeItem : undefined}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemUser");
}}
/>
) : activeTab == "groups" ? (
<CometChatGroups
activeGroup={activeItem instanceof CometChat.Group ? activeItem : undefined}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemGroup");
}}
/>
) : null}
</>}
<CometChatTabs activeTab={activeTab} onTabClicked={(item: { name: string; icon?: string; }) => {
setActiveTab(item.name.toLowerCase())
}} />
</>
);
}
.selector-wrapper .cometchat-conversations .cometchat-list__header-menu .cometchat-button__icon {
background: var(--cometchat-icon-color-primary);
}
.cometchat-conversations .cometchat-list__header-menu .cometchat-button__icon:hover {
background: var(--cometchat-icon-color-highlight);
}
.cometchat-list__header-search-bar {
border-right: none;
}
.cometchat .cometchat-menu-list__sub-menu-list-item {
text-align: left;
}
.cometchat .cometchat-conversations .cometchat-menu-list__sub-menu-list {
width: 212px;
top: 40px !important;
left: 172px !important;
}
#logged-in-user {
border-bottom: 2px solid var(--cometchat-border-color-default, #E8E8E8);
}
#logged-in-user .cometchat-menu-list__sub-menu-item-title,
#logged-in-user .cometchat-menu-list__sub-menu-list-item {
cursor: default;
}
.cometchat-menu-list__sub-menu-list-item-icon-log-out {
background-color: var(--cometchat-error-color, #F44649);
}
.cometchat-menu-list__sub-menu-item-title-log-out {
color: var(--cometchat-error-color, #F44649)
}
.chat-menu .cometchat .cometchat-menu-list__sub-menu-item-title {
cursor: pointer;
}
.chat-menu .cometchat .cometchat-menu-list__sub-menu {
box-shadow: none;
}
.chat-menu .cometchat .cometchat-menu-list__sub-menu-icon {
background-color: var(--cometchat-icon-color-primary, #141414);
width: 24px;
height: 24px;
}
Step 3: Update App
Now we will update the App.tsx
& App.css
files to import these new components as below,
- TypeScript
- CSS
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() {
const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>(undefined);
const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>(undefined);
const [selectedCall, setSelectedCall] = useState<CometChat.Call | undefined>(undefined);
return (
<div className="conversations-with-messages">
<div className="conversations-wrapper">
<CometChatSelector onSelectorItemClicked={(activeItem) => {
let item = activeItem;
if (activeItem instanceof CometChat.Conversation) {
item = activeItem.getConversationWith();
}
if (item instanceof CometChat.User) {
setSelectedUser(item as CometChat.User);
setSelectedCall(undefined);
setSelectedGroup(undefined);
} else if (item instanceof CometChat.Group) {
setSelectedUser(undefined);
setSelectedGroup(item as CometChat.Group);
setSelectedCall(undefined);
}
else if (item instanceof CometChat.Call) {
//custom logic to open call details
setSelectedUser(undefined);
setSelectedGroup(undefined);
setSelectedCall(item as CometChat.Call);
}
}} />
</div>
{selectedUser || selectedGroup || selectedCall ? (
<div className="messages-wrapper">
<CometChatMessageHeader user={selectedUser} group={selectedGroup} />
<CometChatMessageList user={selectedUser} group={selectedGroup} />
<CometChatMessageComposer user={selectedUser} group={selectedGroup} />
</div>
) : (
<div className="empty-conversation">Select Conversation to start</div>
)}
</div>
);
};
export default App;
@import url('../node_modules/@cometchat/chat-uikit-react/dist/styles/css-variables.css');
#root {
text-align: center;
width: 100vw;
height: 100vh;
background-color: #282c34;
}
.conversations-with-messages {
display: flex;
height: 100%;
width: 100%;
flex-direction: row;
}
.conversations-wrapper {
height: 100vh;
width: 480px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.conversations-wrapper>.cometchat {
overflow: hidden;
}
.messages-wrapper {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
}
.empty-conversation {
height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background: var(--cometchat-background-color-03, #F5F5F5);
color: var(--cometchat-text-color-secondary, #727272);
font: var(--cometchat-font-body-regular, 400 14px Roboto);
}
.cometchat .cometchat-message-composer {
border-radius: 0px;
}
Step 4: Run the project
npm start
Next Steps
Enhance the User Experience
- Advanced Customizations – Personalize the chat UI to align with your brand.