Skip to main content
The Conversation List + Message View layout provides a familiar two‑panel experience, similar to WhatsApp Web or Slack. Users browse conversations on the left and chat in real time on the right.

User Interface Overview

Two-panel conversation list and message view
This layout includes:
  1. Sidebar (Conversation List) – Users and groups
  2. Message View – Real-time messages for the selected conversation
  3. Message Composer – Input for sending text and media

Prerequisites

  • Astro project set up
  • React integration added to Astro
  • CometChat credentials
1

Create or open an Astro project

  • npm
npm create astro@latest
cd my-astro-app
npm install
If you already have the sample astro-conversation project, open it instead.
2

Add React and install CometChat UI Kit

npx astro add react
npm i @cometchat/chat-uikit-react react react-dom
Add your CometChat credentials to .env at the project root:
PUBLIC_COMETCHAT_APP_ID=your_app_id
PUBLIC_COMETCHAT_REGION=your_region
PUBLIC_COMETCHAT_AUTH_KEY=your_auth_key
# Optional: default login UID for development
PUBLIC_COMETCHAT_LOGIN_UID=cometchat-uid-3
Use Auth Tokens in production instead of Auth Keys.
3

Initialize CometChat (src/lib/cometchat-init.js)

Create src/lib/cometchat-init.js and initialize the UI Kit using environment variables.
src/lib/cometchat-init.js
import { CometChatUIKit, UIKitSettingsBuilder } from "@cometchat/chat-uikit-react";

const APP_ID   = import.meta.env.PUBLIC_COMETCHAT_APP_ID;
const REGION   = import.meta.env.PUBLIC_COMETCHAT_REGION;
const AUTH_KEY = import.meta.env.PUBLIC_COMETCHAT_AUTH_KEY;

export async function initCometChat() {
  if (!APP_ID || !REGION || !AUTH_KEY) {
    throw new Error("Missing PUBLIC_COMETCHAT_* env vars.");
  }

  const settings = new UIKitSettingsBuilder()
    .setAppId(APP_ID)
    .setRegion(REGION)
    .setAuthKey(AUTH_KEY) // use Auth Tokens in prod
    .subscribePresenceForAllUsers()
    .build();

  await CometChatUIKit.init(settings);
}

export async function ensureLogin(uid) {
  const existing = await CometChatUIKit.getLoggedinUser();
  
  // If a different user is logged in, logout first
  if (existing && existing.getUid() !== uid) {
    console.log(`Switching from user ${existing.getUid()} to ${uid}`);
    await CometChatUIKit.logout();
  }
  
  // Login if no user is logged in or we just logged out
  const currentUser = await CometChatUIKit.getLoggedinUser();
  if (!currentUser) {
    await CometChatUIKit.login(uid);
    console.log(`Logged in as user: ${uid}`);
  } else {
    console.log(`Already logged in as user: ${uid}`);
  }
}

export async function logout() {
  await CometChatUIKit.logout();
}
4

Build the React island (src/components/ChatApp.jsx)

Create the island used by Astro to render the two‑panel chat. This component mirrors the sample in astro-conversation.
src/components/ChatApp.jsx
import { useEffect, useState } from "react";
import {
  CometChatConversations,
  CometChatMessageHeader,
  CometChatMessageList,
  CometChatMessageComposer,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";
import { initCometChat, ensureLogin } from "../lib/cometchat-init.js";

const LOGIN_UID = "cometchat-uid-3";

export default function ChatApp() {
  const [phase, setPhase] = useState("boot"); // boot | ready | error
  const [errorMsg, setErrorMsg] = useState("");
  const [selectedUser, setSelectedUser] = useState(null);
  const [selectedGroup, setSelectedGroup] = useState(null);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        console.log(`Initializing CometChat and logging in as: ${LOGIN_UID}`);
        await initCometChat();
        await ensureLogin(LOGIN_UID);

        if (!cancelled) setPhase("ready");
      } catch (e) {
        console.error("CometChat init/login error:", e);
        if (!cancelled) {
          setErrorMsg(String(e?.message || e));
          setPhase("error");
        }
      }
    })();

    return () => { cancelled = true; };
  }, []);

  const handleConversationClick = (activeItem) => {
    const maybeConv = activeItem?.getConversationWith ? activeItem.getConversationWith() : null;
    const item = maybeConv || activeItem;

    if (item?.getUid) {          // user
      setSelectedUser(item);
      setSelectedGroup(null);
    } else if (item?.getGuid) {  // group
      setSelectedGroup(item);
      setSelectedUser(null);
    } else {
      setSelectedUser(null);
      setSelectedGroup(null);
    }
  };

  if (phase === "boot") return <div style={{ padding: 16 }}>Loading chat…</div>;
  if (phase === "error") {
    return (
      <div style={{ padding: 16, color: "crimson" }}>
        <b>CometChat error:</b> {errorMsg}
      </div>
    );
  }

  return (
    <div className="conversations-with-messages">
      {/* LEFT: conversations */}
      <div className="conversations-wrapper">
        <CometChatConversations onItemClick={handleConversationClick} />
      </div>

      {/* RIGHT: header + list (scrolling) + composer */}
      <div className="messages-wrapper">
        {selectedUser || selectedGroup ? (
          <>
            <CometChatMessageHeader user={selectedUser} group={selectedGroup} />

            <div className="message-list-slot">
              <CometChatMessageList user={selectedUser} group={selectedGroup} />
            </div>

            <CometChatMessageComposer user={selectedUser} group={selectedGroup} />
          </>
        ) : (
          <div className="empty-conversation">Select a conversation to start</div>
        )}
      </div>
    </div>
  );
}
5

Render the page (src/pages/index.astro)

Import the island and styles, then hydrate the component on the client.
src/pages/index.astro
---
import ChatApp from "../components/ChatApp.jsx";
import "../styles/globals.css";
import "../styles/cometchat-layout.css";
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CometChat + Astro</title>
  </head>
  <body>
    <ChatApp client:only="react" />
  </body>
</html>
The CSS files globals.css and cometchat-layout.css are included in the sample. Ensure your layout CSS sets a two‑panel flex container and sizes the sidebar.
6

Run and verify

npm run dev
Open your app and verify you can select conversations on the left and exchange messages on the right.

Troubleshooting

Ensure the component is rendered as a React island (client:only=\"react\") so it runs only in the browser.
Verify .env contains PUBLIC_COMETCHAT_APP_ID, PUBLIC_COMETCHAT_REGION, and PUBLIC_COMETCHAT_AUTH_KEY, and restart the dev server after changes.
The sample uses ensureLogin(uid) to switch users by logging out if the active UID differs. Update PUBLIC_COMETCHAT_LOGIN_UID for development.

Next Steps

  • Customize styles in src/styles/cometchat-layout.css
  • Add presence or typing indicators
  • Explore themes and component overrides in the UI Kit
To build other experiences (One‑to‑One or Tab‑based), reuse src/lib/cometchat-init.js and switch the React island component.