Skip to main content
{
  "component": "CometChatThreadHeader",
  "package": "@cometchat/chat-uikit-react",
  "import": "import { CometChatThreadHeader } from \"@cometchat/chat-uikit-react\";",
  "cssImport": "import \"@cometchat/chat-uikit-react/css-variables.css\";",
  "description": "Displays the parent message and reply count at the top of a threaded conversation view.",
  "cssRootClass": ".cometchat-thread-header",
  "props": {
    "data": {
      "parentMessage": { "type": "CometChat.BaseMessage", "required": true },
      "template": { "type": "CometChatMessageTemplate", "default": "undefined" }
    },
    "callbacks": {
      "onClose": "() => void",
      "onError": "((error: CometChat.CometChatException) => void) | null"
    },
    "visibility": {
      "hideDate": { "type": "boolean", "default": false },
      "hideReplyCount": { "type": "boolean", "default": false },
      "hideReceipts": { "type": "boolean", "default": false }
    }
  }
}

Overview

Threaded messages let users reply to specific messages, keeping related discussions organized. You’ll build a thread panel that shows the parent message and its replies. Time estimate: 15 minutes
Difficulty: Intermediate

Prerequisites

Steps

Step 1: Create the thread panel layout

Combine CometChatThreadHeader, CometChatMessageList, and CometChatMessageComposer with a parentMessageId:
import { useEffect, useState } from "react";
import {
  CometChatThreadHeader,
  CometChatMessageList,
  CometChatMessageComposer,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

interface ThreadViewProps {
  parentMessageId: number;
  user: CometChat.User;
  onClose: () => void;
}

function ThreadView({ parentMessageId, user, onClose }: ThreadViewProps) {
  const [parentMessage, setParentMessage] = useState<CometChat.BaseMessage>();

  useEffect(() => {
    CometChat.getMessageDetails(String(parentMessageId)).then(setParentMessage);
  }, [parentMessageId]);

  if (!parentMessage) return null;

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
      <CometChatThreadHeader
        parentMessage={parentMessage}
        onClose={onClose}
      />
      <div style={{ flex: 1, overflow: "hidden" }}>
        <CometChatMessageList user={user} parentMessageId={parentMessageId} />
      </div>
      <CometChatMessageComposer user={user} parentMessageId={parentMessageId} />
    </div>
  );
}

export default ThreadView;
Thread view with parent message and replies

Step 2: Integrate with your chat layout

Add a side panel that opens when a user clicks “Reply in thread”:
import { useState } from "react";
import {
  CometChatMessageList,
  CometChatMessageComposer,
  CometChatMessageHeader,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import ThreadView from "./ThreadView";

function ChatWithThreads({ user }: { user: CometChat.User }) {
  const [threadMessageId, setThreadMessageId] = useState<number | null>(null);

  return (
    <div style={{ display: "flex", height: "100vh" }}>
      {/* Main chat */}
      <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
        <CometChatMessageHeader user={user} />
        <div style={{ flex: 1 }}>
          <CometChatMessageList 
            user={user}
            // Handle thread open from message actions
          />
        </div>
        <CometChatMessageComposer user={user} />
      </div>

      {/* Thread panel */}
      {threadMessageId && (
        <div style={{ width: 400, borderLeft: "1px solid #e8e8e8" }}>
          <ThreadView
            parentMessageId={threadMessageId}
            user={user}
            onClose={() => setThreadMessageId(null)}
          />
        </div>
      )}
    </div>
  );
}

Step 3: Handle thread header callbacks

Add event handlers for user interactions:
import { CometChatThreadHeader } from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function ThreadHeaderWithCallbacks({ 
  parentMessage,
  onClose 
}: { 
  parentMessage: CometChat.BaseMessage;
  onClose: () => void;
}) {
  return (
    <CometChatThreadHeader
      parentMessage={parentMessage}
      onClose={onClose}
      onSubtitleClicked={() => {
        console.log("Subtitle clicked - scroll to parent in main chat");
      }}
      onError={(error: CometChat.CometChatException) => {
        console.error("Thread error:", error);
      }}
    />
  );
}

Step 4: Customize the thread header

Hide elements or customize date formats:
import {
  CometChatThreadHeader,
  CalendarObject,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function CustomThreadHeader({ parentMessage }: { parentMessage: CometChat.BaseMessage }) {
  return (
    <CometChatThreadHeader
      parentMessage={parentMessage}
      hideDate={false}
      hideReplyCount={false}
      hideReceipts={true}
      separatorDateTimeFormat={new CalendarObject({
        today: "hh:mm A",
        yesterday: "[Yesterday]",
        otherDays: "DD/MM/YYYY",
      })}
    />
  );
}

Step 5: Style the thread panel

Apply custom CSS to match your brand:
/* Thread header styling */
.cometchat-thread-header {
  background-color: #f8f9fa;
}

.cometchat-thread-header__reply-bar-count {
  color: #6852d6;
  font-weight: 600;
}

.cometchat-thread-header__reply-bar-divider {
  background: #6852d6;
}

.cometchat-thread-header__top-bar-title {
  font-size: 16px;
  font-weight: 700;
}
Styled thread header

Complete Example

import { useEffect, useState } from "react";
import {
  CometChatThreadHeader,
  CometChatMessageList,
  CometChatMessageComposer,
  CometChatMessageHeader,
  CalendarObject,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

function ThreadedChat() {
  const [user, setUser] = useState<CometChat.User>();
  const [threadMessageId, setThreadMessageId] = useState<number | null>(null);
  const [parentMessage, setParentMessage] = useState<CometChat.BaseMessage>();

  useEffect(() => {
    CometChat.getUser("USER_UID").then(setUser);
  }, []);

  useEffect(() => {
    if (threadMessageId) {
      CometChat.getMessageDetails(String(threadMessageId)).then(setParentMessage);
    } else {
      setParentMessage(undefined);
    }
  }, [threadMessageId]);

  if (!user) return <div>Loading...</div>;

  return (
    <div style={{ display: "flex", height: "100vh" }}>
      {/* Main chat panel */}
      <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
        <CometChatMessageHeader user={user} />
        <div style={{ flex: 1, overflow: "hidden" }}>
          <CometChatMessageList user={user} />
        </div>
        <CometChatMessageComposer user={user} />
      </div>

      {/* Thread panel */}
      {threadMessageId && parentMessage && (
        <div style={{ 
          width: 400, 
          borderLeft: "1px solid #e8e8e8",
          display: "flex",
          flexDirection: "column"
        }}>
          <CometChatThreadHeader
            parentMessage={parentMessage}
            onClose={() => setThreadMessageId(null)}
            separatorDateTimeFormat={new CalendarObject({
              today: "hh:mm A",
              yesterday: "[Yesterday]",
              otherDays: "MMM DD, YYYY",
            })}
          />
          <div style={{ flex: 1, overflow: "hidden" }}>
            <CometChatMessageList 
              user={user} 
              parentMessageId={threadMessageId} 
            />
          </div>
          <CometChatMessageComposer 
            user={user} 
            parentMessageId={threadMessageId} 
          />
        </div>
      )}
    </div>
  );
}

export default ThreadedChat;
Replace the default parent message display:
<CometChatThreadHeader
  parentMessage={parentMessage}
  messageBubbleView={
    <div style={{ 
      padding: 16, 
      background: "#f0f0f0", 
      borderRadius: 8,
      margin: 8
    }}>
      <strong>{parentMessage.getSender().getName()}</strong>
      <p>{(parentMessage as CometChat.TextMessage).getText()}</p>
    </div>
  }
/>

Common Issues

The parentMessageId must be passed to both CometChatMessageList and CometChatMessageComposer for replies to be associated with the correct thread.
IssueSolution
Replies not showing in threadVerify parentMessageId is passed to MessageList
Reply count not updatingComponent auto-subscribes to SDK events - check connection
Thread panel not closingEnsure onClose callback updates your state

Next Steps