The Conversation List + Message View layout offers a seamless two-panel chat interface, commonly used in modern messaging applications like WhatsApp Web, Slack, and Microsoft Teams. This design enables users to switch between conversations effortlessly while keeping the chat window open, ensuring a smooth, real-time messaging experience.
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 Overview

This layout is structured into three key sections:
  1. Sidebar (Conversation List) – Displays active conversations, including users and groups.
  2. Message View – Shows chat messages for the selected conversation in real-time.
  3. Message Composer – Provides an input field for typing and sending messages, along with support for media, emojis, and reactions.

Step-by-Step Guide

Step 1: 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

Download the Icon

These icons are available in the CometChat UI Kit assets folder. You can find them at:
🔗 GitHub Assets Folder
CometChatSelector.tsx
import { useEffect, useState } from "react";
import { Conversation, Group, User } from "@cometchat/chat-sdk-javascript";
import { CometChatConversations, CometChatUIKitLoginListener } from "@cometchat/chat-uikit-react";
import { CometChat } from '@cometchat/chat-sdk-javascript';
import "./CometChatSelector.css";

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

// CometChatSelector component definition
export const CometChatSelector = (props: SelectorProps) => {
    const {
        onSelectorItemClicked = () => { }, // Default function if no prop is provided
    } = props;

    // State to store the currently logged-in user
    const [loggedInUser, setLoggedInUser] = useState<CometChat.User | null>();
    
    // State to track the currently selected item (conversation, user, group, or call)
    const [activeItem, setActiveItem] = useState<
        CometChat.Conversation | CometChat.User | CometChat.Group | CometChat.Call | undefined
    >();

    useEffect(() => {
        // Retrieve the logged-in user from CometChat's login listener
        let loggedInUser = CometChatUIKitLoginListener.getLoggedInUser();
        setLoggedInUser(loggedInUser);
    }, [CometChatUIKitLoginListener?.getLoggedInUser()]); // Dependency array to trigger effect when user changes

    return (
        <>
            {/* Render CometChatConversations only if a user is logged in */}
            {loggedInUser && (
                <>
                    <CometChatConversations
                        activeConversation={activeItem instanceof CometChat.Conversation ? activeItem : undefined}
                        onItemClick={(e) => {
                            setActiveItem(e); // Update the selected item state
                            onSelectorItemClicked(e, "updateSelectedItem"); // Trigger callback with selected item
                        }}
                    />
                </>
            )}
        </>
    );
}; 

Step 2: Render Experience

Now we will create the CometChatNoSSR.tsx & CometChatNoSSR.css files. Here, we will initialize the CometChat UI Kit, log in a user, and build the messaging experience using CometChatMessageHeader, CometChatMessageList, and CometChatMessageComposer components.
src/
│── CometChatNoSSR/
│   ├── CometChatNoSSR.tsx
│   ├── CometChatNoSSR.css
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
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("cometchat-uid-1")
              .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 3: 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";

import '@cometchat/chat-uikit-react/css-variables.css';

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

// Dynamically import CometChat component with SSR disabled
const CometChatComponent = dynamic(() => import("../CometChatNoSSR/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 4: Update Global CSS

Next, add the following styles to global.css to ensure CometChat UI Kit is properly styled.
global.css
:root {
  --background: #ffffff;
  --foreground: #171717;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

/** Give your App a height of `100%`. Keep other CSS properties in the below selector as it is. */
.root {
  height: 100%;
}

#__next {
  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;
  }
}

Step 5: Run the project

npm run dev

Next Steps

Enhance the User Experience