Skip to main content
Version: v6

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.


User Interface Preview

Image

This layout consists of:

  1. Sidebar (Conversation List) – Displays recent conversations with active users and groups.
  2. Message View – Shows the selected chat with real-time messages.
  3. 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/app
│── 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

CometChatTabs.tsx
import { useState } from "react";
import "./CometChatTabs.css";

// Define icon paths for tabs
const chatsIcon = "/assets/chats.svg";
const callsIcon = "/assets/calls.svg";
const usersIcon = "/assets/users.svg";
const groupsIcon = "/assets/groups.svg";

// CometChatTabs component to display different tab options
export const CometChatTabs = (props: {
onTabClicked?: (tabItem: { name: string; icon?: string; }) => void; // Callback when a tab is clicked
activeTab?: string; // Currently active tab
}) => {

const {
onTabClicked = () => { }, // Default function if no prop is provided
activeTab
} = props;

// State to track the currently hovered tab
const [hoverTab, setHoverTab] = useState("");

// Array of tab items with their respective names and icons
const tabItems = [
{
"name": "CHATS",
"icon": chatsIcon // Icon for Chats tab
},
{
"name": "CALLS",
"icon": callsIcon // Icon for Calls tab
},
{
"name": "USERS",
"icon": usersIcon // Icon for Users tab
},
{
"name": "GROUPS",
"icon": groupsIcon // Icon for Groups tab
}
]

return (
<div className="cometchat-tab-component">
{
// Loop through tab items and render each tab
tabItems.map((tabItem) => (
<div
key={tabItem.name}
className="cometchat-tab-component__tab"
onClick={() => onTabClicked(tabItem)} // Handle tab click event
>
{/* Tab Icon */}
<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={{
WebkitMaskImage: `url(${tabItem.icon})`, // Apply mask for Webkit browsers
maskImage: `url(${tabItem.icon})` // Standard mask image
}}
onMouseEnter={() => setHoverTab(tabItem.name.toLowerCase())} // Track mouse hover
onMouseLeave={() => setHoverTab("")}
/>
{/* Tab Text */}
<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} {/* Display tab name */}
</div>
</div>
))
}
</div>
);
};

Step 2: Create Sidebar

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

Folder Structure

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

src/app
│── CometChatSelector/
│ ├── CometChatSelector.tsx
│ ├── CometChatSelector.css
CometChatSelector.tsx
import { useEffect, useState } from "react";
import { Call, Conversation, Group, User } from "@cometchat/chat-sdk-javascript";
import {
CometChatCallLogs,
CometChatConversations,
CometChatGroups,
CometChatUIKit,
CometChatUIKitLoginListener,
CometChatUsers
} from "@cometchat/chat-uikit-react";
import { CometChatTabs } from "../CometChatTabs/CometChatTabs";
import { CometChat } from "@cometchat/chat-sdk-javascript";

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

// CometChatSelector component to handle different chat elements
export const CometChatSelector = (props: SelectorProps) => {
const {
onSelectorItemClicked = () => { }, // Default callback function
} = props;

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

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

// State to track the active tab
const [activeTab, setActiveTab] = useState<string>("chats");

// Effect to retrieve the logged-in user on component mount
useEffect(() => {
let loggedInUsers = CometChatUIKitLoginListener.getLoggedInUser();
setLoggedInUser(loggedInUsers);
}, [CometChatUIKitLoginListener?.getLoggedInUser()]);

// Function to log out the user
const logOut = () => {
CometChatUIKit.logout()
.then(() => {
setLoggedInUser(null);
})
.catch((error) => {
console.log("error", error);
});
};

return (
<>
{loggedInUser && (
<>
{/* Render different components based on the active tab */}
{activeTab == "chats" ? (
<CometChatConversations
activeConversation={activeItem instanceof CometChat.Conversation ? activeItem : undefined}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItem");
}}
/>
) : activeTab == "calls" ? (
<CometChatCallLogs
activeCall={activeItem as Call}
onItemClick={(e: Call) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemCall");
}}
/>
) : activeTab == "users" ? (
<CometChatUsers
activeUser={activeItem as User}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemUser");
}}
/>
) : activeTab == "groups" ? (
<CometChatGroups
activeGroup={activeItem as Group}
onItemClick={(e) => {
setActiveItem(e);
onSelectorItemClicked(e, "updateSelectedItemGroup");
}}
/>
) : null}
</>
)}
{/* Render the tabs component for navigation */}
<CometChatTabs
activeTab={activeTab}
onTabClicked={(item) => {
setActiveTab(item.name.toLowerCase());
}}
/>
</>
);
};

Step 3: Render Experience

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

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

// Constants for CometChat configuration
const COMETCHAT_CONSTANTS = {
APP_ID: "",
REGION: "",
AUTH_KEY: "",
};

// Functional component for CometChatNoSSR
const CometChatNoSSR: React.FC = () => {
// State to store the logged-in user
const [user, setUser] = useState<CometChat.User | undefined>(undefined);
// State to store selected user or group
const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>(undefined);
const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>(undefined);

useEffect(() => {
// Initialize UIKit settings
const UIKitSettings = new UIKitSettingsBuilder()
.setAppId(COMETCHAT_CONSTANTS.APP_ID)
.setRegion(COMETCHAT_CONSTANTS.REGION)
.setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY)
.subscribePresenceForAllUsers()
.build();

// Initialize CometChat UIKit
CometChatUIKit.init(UIKitSettings)
?.then(() => {
console.log("Initialization completed successfully");
// Check if user is already logged in
CometChatUIKit.getLoggedinUser().then((loggedInUser) => {
if (!loggedInUser) {
// Perform login if no user is logged in
CometChatUIKit.login("superhero1")
.then((user) => {
console.log("Login Successful", { user });
setUser(user);
})
.catch((error) => console.error("Login failed", error));
} else {
console.log("Already logged-in", { loggedInUser });
setUser(loggedInUser);
}
});
})
.catch((error) => console.error("Initialization failed", error));
}, []);

return user ? (
<div className="conversations-with-messages">
{/* Sidebar with conversation list */}
<div className="conversations-wrapper">
<CometChatSelector
onSelectorItemClicked={(activeItem) => {
let item = activeItem;
// Extract the conversation participant
if (activeItem instanceof CometChat.Conversation) {
item = activeItem.getConversationWith();
}
// Update states based on the type of selected item
if (item instanceof CometChat.User) {
setSelectedUser(item as CometChat.User);
setSelectedGroup(undefined);
} else if (item instanceof CometChat.Group) {
setSelectedUser(undefined);
setSelectedGroup(item as CometChat.Group);
} else {
setSelectedUser(undefined);
setSelectedGroup(undefined);
}
}}
/>
</div>

{/* Message view section */}
{selectedUser || selectedGroup ? (
<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>
) : undefined;
};

export default CometChatNoSSR;

Step 4: Disabling SSR for CometChatNoSSR.tsx in Next.js

In this update, we will disable Server-Side Rendering (SSR) for CometChatNoSSR.tsx while keeping the rest of the application’s SSR functionality intact. This ensures that the CometChat UI Kit components load only on the client-side, preventing SSR-related issues.

Disabling SSR in index.tsx

Modify your index.tsx file to dynamically import the CometChatNoSSR.tsx component with { ssr: false }.

index.tsx
import { Inter } from "next/font/google";
import dynamic from "next/dynamic";

const inter = Inter({ subsets: ["latin"] });

// Dynamically import CometChat component with SSR disabled
const CometChatComponent = dynamic(() => import("./CometChatNoSSR"), {
ssr: false,
});

export default function Home() {
return <CometChatComponent />;
}

Why disable SSR?

  • The CometChat UI Kit depends on browser APIs (window, document, WebSockets).
  • Next.js pre-renders components on the server, which can cause errors with browser-specific features.
  • By setting { ssr: false }, we ensure that CometChatNoSSR.tsx only loads on the client.

Step 5: Import CometChat UI Kit CSS

Next, add the following import statement to global.css to ensure CometChat UI Kit styles are included.

global.css
@import url("../../node_modules/@cometchat/chat-uikit-react/dist/styles/css-variables.css");

Step 6: Run the project

npm start

Next Steps

Enhance the User Experience