Skip to main content

Documentation Index

Fetch the complete documentation index at: https://cometchat-22654f5b-react-uikit-v7.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

FieldValue
Package@cometchat/chat-uikit-react
FrameworkAstro
ComponentsCometChatConversations, CometChatCallLogs, CometChatUsers, CometChatMessageHeader, CometChatMessageList, CometChatMessageComposer
LayoutTabbed sidebar (Chat, Calls, Users) + message view
PrerequisiteComplete Astro Integration first
SSRReact island with client:only="react" directive
PatternFull-featured messaging app with multiple sections
This guide builds a tabbed messaging UI — Chat, Calls, and Users tabs in the sidebar, with a message view on the right. Good for full-featured apps that need more than just conversations. This assumes you’ve already completed Astro Integration (project created, UI Kit installed, init + login working).

What You’re Building

Three sections working together:
  1. Tab bar — switches between Chat, Calls, and Users
  2. Sidebar — renders the list for the active tab
  3. Message view — header + messages + composer for the selected item

Full Code

Create a React island component with the tabbed UI, then render it in an Astro page with client:only="react".
src/components/TabbedChat.tsx
import { useState } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
  CometChatProvider,
  CometChatConversations,
  CometChatUsers,
  CometChatCallLogs,
  CometChatMessageHeader,
  CometChatMessageList,
  CometChatMessageComposer,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/styles";

type Tab = "chat" | "calls" | "users";

export default function TabbedChat() {
  const [activeTab, setActiveTab] = useState<Tab>("chat");
  const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>(undefined);
  const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>(undefined);

  const handleConversationClick = (conversation: CometChat.Conversation) => {
    const entity = conversation.getConversationWith();
    if (conversation.getConversationType() === "user") {
      setSelectedUser(entity as CometChat.User);
      setSelectedGroup(undefined);
    } else {
      setSelectedGroup(entity as CometChat.Group);
      setSelectedUser(undefined);
    }
  };

  const handleUserClick = (user: CometChat.User) => {
    setSelectedUser(user);
    setSelectedGroup(undefined);
  };

  const handleCallClick = (call: any) => {
    const initiator = call.getInitiator();
    const receiver = call.getReceiver();

    // Determine the other party in the call
    if (receiver instanceof CometChat.User) {
      setSelectedUser(receiver);
      setSelectedGroup(undefined);
    } else if (receiver instanceof CometChat.Group) {
      setSelectedUser(undefined);
      setSelectedGroup(receiver);
    } else if (initiator instanceof CometChat.User) {
      setSelectedUser(initiator);
      setSelectedGroup(undefined);
    }
  };

  return (
    <CometChatProvider>
      <div style={{ display: "flex", height: "100vh", width: "100%" }}>
        <div style={{
          width: 360,
          height: "100%",
          display: "flex",
          flexDirection: "column",
          borderRight: "1px solid #eee",
        }}>
          <div style={{
            display: "flex",
            borderBottom: "1px solid #eee",
            background: "#fff",
          }}>
            <button
              style={{
                flex: 1,
                padding: "12px 0",
                border: "none",
                background: "none",
                cursor: "pointer",
                fontWeight: 500,
                fontSize: 14,
                color: activeTab === "chat" ? "#6851d6" : "#727272",
                borderBottom: activeTab === "chat" ? "2px solid #6851d6" : "2px solid transparent",
              }}
              onClick={() => setActiveTab("chat")}
            >
              Chats
            </button>
            <button
              style={{
                flex: 1,
                padding: "12px 0",
                border: "none",
                background: "none",
                cursor: "pointer",
                fontWeight: 500,
                fontSize: 14,
                color: activeTab === "calls" ? "#6851d6" : "#727272",
                borderBottom: activeTab === "calls" ? "2px solid #6851d6" : "2px solid transparent",
              }}
              onClick={() => setActiveTab("calls")}
            >
              Calls
            </button>
            <button
              style={{
                flex: 1,
                padding: "12px 0",
                border: "none",
                background: "none",
                cursor: "pointer",
                fontWeight: 500,
                fontSize: 14,
                color: activeTab === "users" ? "#6851d6" : "#727272",
                borderBottom: activeTab === "users" ? "2px solid #6851d6" : "2px solid transparent",
              }}
              onClick={() => setActiveTab("users")}
            >
              Users
            </button>
          </div>

          <div style={{ flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }}>
            {activeTab === "chat" && (
              <CometChatConversations onItemClick={handleConversationClick} />
            )}
            {activeTab === "calls" && (
              <CometChatCallLogs onItemClick={handleCallClick} />
            )}
            {activeTab === "users" && (
              <CometChatUsers onItemClick={handleUserClick} />
            )}
          </div>
        </div>

        {selectedUser || selectedGroup ? (
          <div style={{ flex: 1, height: "100%", display: "flex", flexDirection: "column" }}>
            <CometChatMessageHeader user={selectedUser} group={selectedGroup} />
            <CometChatMessageList user={selectedUser} group={selectedGroup} />
            <CometChatMessageComposer user={selectedUser} group={selectedGroup} />
          </div>
        ) : (
          <div style={{
            flex: 1,
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            background: "#f5f5f5",
            color: "#727272",
            font: "400 14px Roboto, sans-serif",
          }}>
            Select a conversation to start chatting
          </div>
        )}
      </div>
    </CometChatProvider>
  );
}
src/pages/chat.astro
---
import TabbedChat from '../components/TabbedChat.tsx';
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Chat</title>
  </head>
  <body style="margin: 0;">
    <TabbedChat client:only="react" />
  </body>
</html>

How It Works

  1. client:only="react" tells Astro to skip server-rendering and hydrate the component entirely on the client. This is required because CometChat uses browser APIs.
  2. React island — the CometChat UI lives in a self-contained React component. Astro handles the page shell, React handles the interactive chat.
  3. Tab stateactiveTab controls which list component renders in the sidebar.
  4. Conditional rendering — only the active tab’s component mounts. Switching tabs unmounts the previous list and mounts the new one.
  5. Unified selection — all three tabs feed into the same selectedUser / selectedGroup state. Clicking any item (conversation, call log, or user) updates the message panel.
  6. Call log handling — when a call log is clicked, the receiver (user or group) is extracted and passed to the message components.

Adding More Tabs

To add a Groups tab, import CometChatGroups and add another tab button + conditional render:
import { CometChatGroups } from "@cometchat/chat-uikit-react";

// Add to Tab type:
type Tab = "chat" | "calls" | "users" | "groups";

// Add button in the tab bar:
<button
  style={{
    flex: 1,
    padding: "12px 0",
    border: "none",
    background: "none",
    cursor: "pointer",
    fontWeight: 500,
    fontSize: 14,
    color: activeTab === "groups" ? "#6851d6" : "#727272",
    borderBottom: activeTab === "groups" ? "2px solid #6851d6" : "2px solid transparent",
  }}
  onClick={() => setActiveTab("groups")}
>
  Groups
</button>

// Add in the sidebar list:
{activeTab === "groups" && (
  <CometChatGroups
    onItemClick={(group: CometChat.Group) => {
      setSelectedGroup(group);
      setSelectedUser(undefined);
    }}
  />
)}
You can follow the same pattern for any additional tabs (Settings, Contacts, etc.).

Run

npm run dev
Open http://localhost:4321/chat. You should see the tab bar at the top of the sidebar. Switch between Chats, Calls, and Users — clicking any item loads the message view on the right.

Next Steps

Conversation List + Messages

Two-panel layout without tabs

Components Overview

Browse all prebuilt UI components

Theming

Customize colors, fonts, and styles