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.
Goal
By the end of this guide you will have a chat interface where users can open a thread panel from any message, view the parent message with its reply count, browse threaded replies, and send new replies — all using v7 compound components and a state-based approach (no custom events needed).
Prerequisites
- Completed the Quick Start guide
- A running
CometChatProvider setup with valid credentials
- An existing chat screen using
CometChatMessageList and CometChatMessageComposer
Components Used
| Component | Purpose |
|---|
CometChatThreadHeader | Displays the parent message and reply count at the top of the thread panel |
CometChatMessageList | Renders threaded replies when given a parentMessageId |
CometChatMessageComposer | Sends replies into the thread with parentMessageId, layout="compact", and enableRichTextEditor |
Step 1: Thread State Management
Store the threadedMessage in state. When set, the thread panel renders. When cleared, it closes. This mirrors the pattern used in the sample app’s CometChatThreadPanel component.
File: ChatWithThreads.tsx
import { useState } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
function ChatWithThreads() {
const [user, setUser] = useState<CometChat.User | null>(null);
const [group, setGroup] = useState<CometChat.Group | null>(null);
const [threadedMessage, setThreadedMessage] = useState<CometChat.BaseMessage | null>(null);
// Thread opens when threadedMessage is set, closes when cleared
}
Step 2: Wire the Thread Trigger
Use the onThreadRepliesClick callback on CometChatMessageList to capture when a user clicks “Reply in Thread.” This sets the threaded message and opens the panel — no events required.
File: ChatWithThreads.tsx
import { CometChatMessageList } from "@cometchat/chat-uikit-react";
<CometChatMessageList
user={user ?? undefined}
group={group ?? undefined}
onThreadRepliesClick={(message) => setThreadedMessage(message)}
/>
Step 3: Build the Thread Panel
When threadedMessage is set, render a side panel composing CometChatThreadHeader + CometChatMessageList (with parentMessageId) + CometChatMessageComposer (with parentMessageId, layout="compact", and enableRichTextEditor). The onClose callback clears the state to dismiss the panel.
File: ChatWithThreads.tsx
import {
CometChatThreadHeader,
CometChatMessageList,
CometChatMessageComposer,
} from "@cometchat/chat-uikit-react";
{threadedMessage && (
<div style={{ width: "400px", borderLeft: "1px solid #e0e0e0", display: "flex", flexDirection: "column" }}>
<CometChatThreadHeader
parentMessage={threadedMessage}
onClose={() => setThreadedMessage(null)}
onParentDeleted={() => setThreadedMessage(null)}
/>
<div style={{ flex: 1, overflow: "hidden" }}>
<CometChatMessageList
parentMessageId={threadedMessage.getId()}
user={user ?? undefined}
group={group ?? undefined}
/>
</div>
<CometChatMessageComposer
parentMessageId={threadedMessage.getId()}
user={user ?? undefined}
group={group ?? undefined}
layout="compact"
enableRichTextEditor
/>
</div>
)}
Step 4: Handle Parent Deleted
Use the onParentDeleted prop on CometChatThreadHeader to automatically close the thread panel when the parent message is deleted by another user or a moderation action.
File: ChatWithThreads.tsx
<CometChatThreadHeader
parentMessage={threadedMessage}
onClose={() => setThreadedMessage(null)}
onParentDeleted={() => setThreadedMessage(null)}
/>
Complete Example
File: App.tsx
import { useState } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
CometChatProvider,
CometChatConversations,
CometChatMessageList,
CometChatMessageComposer,
CometChatMessageHeader,
CometChatThreadHeader,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/styles";
function ChatWithThreads() {
const [user, setUser] = useState<CometChat.User | null>(null);
const [group, setGroup] = useState<CometChat.Group | null>(null);
const [threadedMessage, setThreadedMessage] = useState<CometChat.BaseMessage | null>(null);
function handleConversationClick(conversation: CometChat.Conversation) {
setThreadedMessage(null);
const entity = conversation.getConversationWith();
if (entity instanceof CometChat.User) {
setUser(entity);
setGroup(null);
} else if (entity instanceof CometChat.Group) {
setGroup(entity);
setUser(null);
}
}
return (
<div style={{ display: "flex", height: "100vh" }}>
{/* Conversations sidebar */}
<div style={{ width: "300px", borderRight: "1px solid #e0e0e0" }}>
<CometChatConversations onItemClick={handleConversationClick} />
</div>
{/* Main message panel */}
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
{(user || group) && (
<>
<CometChatMessageHeader user={user ?? undefined} group={group ?? undefined} />
<div style={{ flex: 1, overflow: "hidden" }}>
<CometChatMessageList
user={user ?? undefined}
group={group ?? undefined}
onThreadRepliesClick={(message) => setThreadedMessage(message)}
/>
</div>
<CometChatMessageComposer user={user ?? undefined} group={group ?? undefined} />
</>
)}
</div>
{/* Thread panel */}
{threadedMessage && (
<div style={{ width: "400px", borderLeft: "1px solid #e0e0e0", display: "flex", flexDirection: "column" }}>
<CometChatThreadHeader
parentMessage={threadedMessage}
onClose={() => setThreadedMessage(null)}
onParentDeleted={() => setThreadedMessage(null)}
/>
<div style={{ flex: 1, overflow: "hidden" }}>
<CometChatMessageList
parentMessageId={threadedMessage.getId()}
user={user ?? undefined}
group={group ?? undefined}
/>
</div>
<CometChatMessageComposer
parentMessageId={threadedMessage.getId()}
user={user ?? undefined}
group={group ?? undefined}
layout="compact"
enableRichTextEditor
/>
</div>
)}
</div>
);
}
function App() {
return (
<CometChatProvider
appId="YOUR_APP_ID"
region="YOUR_REGION"
authKey="YOUR_AUTH_KEY"
>
<ChatWithThreads />
</CometChatProvider>
);
}
export default App;
Next Steps