AI Agent Component Spec
AI Agent Component Spec
- Package:
@cometchat/chat-uikit-react - Framework: React Router
- Key components:
CometChatConversations+CometChatCallLogs+CometChatUsers+CometChatGroups - Required setup:
CometChatUIKit.init(UIKitSettings)thenCometChatUIKit.login("UID") - Parent guide: React Router Integration
User Interface Preview

- Sidebar (Conversation List) — recent conversations with users and groups
- Message View — messages for the selected chat
- Message Composer — text input with media, emoji, and reaction support
Step-by-Step Guide
Create a Tab Component
Create a
🔗 GitHub Assets Folder
CometChatTabs folder inside src:Report incorrect code
Copy
Ask AI
public/
├── assets
│ ├── chats.svg
│ ├── calls.svg
│ ├── users.svg
│ ├── groups.svg
src/app/
│── CometChatTabs/
│ ├── 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
CometChatTabs.tsx
Report incorrect code
Copy
Ask AI
import { useState } from "react";
import "./CometChatTabs.css";
const chatsIcon = "/assets/chats.svg";
const callsIcon = "/assets/calls.svg";
const usersIcon = "/assets/users.svg";
const groupsIcon = "/assets/groups.svg";
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) => {
const isActive =
activeTab === tabItem.name.toLowerCase() ||
hoverTab === tabItem.name.toLowerCase();
return (
<div
key={tabItem.name}
className="cometchat-tab-component__tab"
onClick={() => onTabClicked(tabItem)}
>
<div
className={
isActive
? "cometchat-tab-component__tab-icon cometchat-tab-component__tab-icon-active"
: "cometchat-tab-component__tab-icon"
}
style={{
WebkitMaskImage: `url(${tabItem.icon})`,
maskImage: `url(${tabItem.icon})`,
}}
onMouseEnter={() => setHoverTab(tabItem.name.toLowerCase())}
onMouseLeave={() => setHoverTab("")}
/>
<div
className={
isActive
? "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>
);
};
CometChatTabs.css
Report incorrect code
Copy
Ask AI
.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;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px 0px 10px 0px;
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);
-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);
}
Create Sidebar
Create a
CometChatSelector folder inside src/app:Report incorrect code
Copy
Ask AI
src/app/
│── CometChatSelector/
│ ├── CometChatSelector.tsx
│ ├── CometChatSelector.css
- TypeScript
- CSS
CometChatSelector.tsx
Report incorrect code
Copy
Ask AI
import { useEffect, useState } from "react";
import {
Call,
Conversation,
Group,
User,
CometChat
} from "@cometchat/chat-sdk-javascript";
import {
CometChatCallLogs,
CometChatConversations,
CometChatGroups,
CometChatUIKit,
CometChatUIKitLoginListener,
CometChatUsers
} from "@cometchat/chat-uikit-react";
import { CometChatTabs } from "../CometChatTabs/CometChatTabs";
interface SelectorProps {
onSelectorItemClicked?: (input: User | Group | Conversation | Call, type: string) => void;
}
export const CometChatSelector = (props: SelectorProps) => {
const {
onSelectorItemClicked = () => {},
} = props;
const [loggedInUser, setLoggedInUser] = useState<CometChat.User | null>();
const [activeItem, setActiveItem] = useState<
Conversation | User | Group | Call | undefined
>();
const [activeTab, setActiveTab] = useState<string>("chats");
useEffect(() => {
const user = CometChatUIKitLoginListener.getLoggedInUser();
setLoggedInUser(user);
}, [CometChatUIKitLoginListener?.getLoggedInUser()]);
const logOut = () => {
CometChatUIKit.logout()
.then(() => setLoggedInUser(null))
.catch((error) => console.log("Logout error:", error));
};
return (
<>
{loggedInUser && (
<>
{activeTab === "chats" && (
<CometChatConversations
activeConversation={activeItem instanceof CometChat.Conversation ? activeItem : undefined}
onItemClick={(item) => {
setActiveItem(item);
onSelectorItemClicked(item, "updateSelectedItem");
}}
/>
)}
{activeTab === "calls" && (
<CometChatCallLogs
activeCall={activeItem as Call}
onItemClick={(item: Call) => {
setActiveItem(item);
onSelectorItemClicked(item, "updateSelectedItemCall");
}}
/>
)}
{activeTab === "users" && (
<CometChatUsers
activeUser={activeItem as User}
onItemClick={(item) => {
setActiveItem(item);
onSelectorItemClicked(item, "updateSelectedItemUser");
}}
/>
)}
{activeTab === "groups" && (
<CometChatGroups
activeGroup={activeItem as Group}
onItemClick={(item) => {
setActiveItem(item);
onSelectorItemClicked(item, "updateSelectedItemGroup");
}}
/>
)}
</>
)}
<CometChatTabs
activeTab={activeTab}
onTabClicked={(item) => setActiveTab(item.name.toLowerCase())}
/>
</>
);
};
CometChatSelector.css
Report incorrect code
Copy
Ask AI
.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;
}
Render Experience
Update
CometChatNoSSR.tsx and CometChatNoSSR.css to import the new components:- TypeScript
- CSS
CometChatNoSSR.tsx
Report incorrect code
Copy
Ask AI
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";
const COMETCHAT_CONSTANTS = {
APP_ID: "", // Replace with your App ID
REGION: "", // Replace with your Region
AUTH_KEY: "", // Replace with your Auth Key
};
const UID = "cometchat-uid-4"; // Replace with dynamic UID
const CometChatNoSSR: React.FC = () => {
const [user, setUser] = useState<CometChat.User | undefined>(undefined);
const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>(undefined);
const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>(undefined);
useEffect(() => {
const UIKitSettings = new UIKitSettingsBuilder()
.setAppId(COMETCHAT_CONSTANTS.APP_ID)
.setRegion(COMETCHAT_CONSTANTS.REGION)
.setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY)
.subscribePresenceForAllUsers()
.build();
CometChatUIKit.init(UIKitSettings)
?.then(() => {
CometChatUIKit.getLoggedinUser().then((loggedInUser) => {
if (!loggedInUser) {
CometChatUIKit.login(UID)
.then((user) => setUser(user))
.catch((error) => console.error("Login failed", error));
} else {
setUser(loggedInUser);
}
});
})
.catch((error) => console.error("Initialization failed", error));
}, []);
return user ? (
<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);
setSelectedGroup(undefined);
} else if (item instanceof CometChat.Group) {
setSelectedUser(undefined);
setSelectedGroup(item as CometChat.Group);
} else {
setSelectedUser(undefined);
setSelectedGroup(undefined);
}
}}
/>
</div>
{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;
CometChatNoSSR.css
Report incorrect code
Copy
Ask AI
.conversations-with-messages {
display: flex;
height: 100%;
width: 100%;
}
.conversations-wrapper {
height: 100%;
width: 480px;
overflow: hidden;
display: flex;
flex-direction: column;
height: inherit;
}
.conversations-wrapper > .cometchat {
overflow: hidden;
}
.messages-wrapper {
width: calc(100% - 480px);
height: 100%;
display: flex;
flex-direction: column;
}
.empty-conversation {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background: white;
color: var(--cometchat-text-color-secondary, #727272);
font: var(--cometchat-font-body-regular, 400 14px Roboto);
}
.cometchat .cometchat-message-composer {
border-radius: 0px;
}
Disable SSR and Render the CometChat Component
Create a file CometChat.tsx inside the routes folder:Create a route for CometChat in the routes file:
Report incorrect code
Copy
Ask AI
import React, { lazy, Suspense, useEffect, useState } from "react";
import "@cometchat/chat-uikit-react/css-variables.css";
const CometChatNoSSR = lazy(() => import("../CometChatNoSSR/CometChatNoSSR"));
export default function CometChatRoute() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted ? (
<Suspense fallback={<div>Loading...</div>}>
<CometChatNoSSR />
</Suspense>
) : (
<div>Loading...</div>
);
}
Report incorrect code
Copy
Ask AI
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("chat", "routes/CometChat.tsx"),
] satisfies RouteConfig;
CometChat UI Kit relies on browser APIs (window, document, WebSockets). Since React Router renders on the server by default, disabling SSR for this component prevents runtime errors.
Update App CSS
Add the following styles to
app.css:app.css
Report incorrect code
Copy
Ask AI
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
.root {
height: 100%;
}
html,
body {
height: 100%;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}