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.

Goal

By the end of this guide you will have a chat interface with a search panel that lets users query messages in a conversation by keyword, displays matching results, and scrolls to a selected message — all using v7 compound components and the CometChat SDK’s MessagesRequestBuilder.

Prerequisites

  • Completed the Quick Start guide
  • A running CometChatProvider setup with valid credentials
  • An existing chat screen using CometChatMessageList and CometChatMessageHeader

Components Used

Component / APIPurpose
CometChatSearch or CometChat.MessagesRequestBuilderSDK builder for querying messages by keyword
CometChatMessageListDisplays messages and supports goToMessageId for scrolling to a result
CometChatMessageHeaderHeader with search trigger button
CometChatConversationsSidebar for switching conversations

Step 1: Set up the chat layout with search state

Start with a full chat layout that includes a conversations sidebar, message panel, and state to control the search panel visibility and the message to jump to. File: App.tsx
import { useState } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
  CometChatProvider,
  CometChatConversations,
  CometChatMessageList,
  CometChatMessageComposer,
  CometChatMessageHeader,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/styles";
import MessageSearchPanel from "./MessageSearchPanel";

function ChatWithSearch() {
  const [user, setUser] = useState<CometChat.User | null>(null);
  const [group, setGroup] = useState<CometChat.Group | null>(null);
  const [showSearch, setShowSearch] = useState(false);
  const [goToMessageId, setGoToMessageId] = useState<number | undefined>(undefined);

  function handleConversationClick(conversation: CometChat.Conversation) {
    setShowSearch(false);
    setGoToMessageId(undefined);
    const entity = conversation.getConversationWith();
    if (entity instanceof CometChat.User) {
      setUser(entity);
      setGroup(null);
    } else if (entity instanceof CometChat.Group) {
      setGroup(entity);
      setUser(null);
    }
  }

  function handleMessageSelect(message: CometChat.BaseMessage) {
    setGoToMessageId(message.getId());
    setShowSearch(false);
  }

  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}
              auxiliaryButtonView={
                <button
                  onClick={() => setShowSearch((prev) => !prev)}
                  aria-label="Search messages"
                  style={{ background: "none", border: "none", cursor: "pointer", fontSize: "18px" }}
                >
                  🔍
                </button>
              }
            />
            <div style={{ flex: 1, overflow: "hidden" }}>
              <CometChatMessageList
                user={user ?? undefined}
                group={group ?? undefined}
                goToMessageId={goToMessageId}
              />
            </div>
            <CometChatMessageComposer user={user ?? undefined} group={group ?? undefined} />
          </>
        )}
      </div>

      {/* Search panel */}
      {showSearch && (user || group) && (
        <MessageSearchPanel
          user={user ?? undefined}
          group={group ?? undefined}
          onMessageSelect={handleMessageSelect}
          onClose={() => setShowSearch(false)}
        />
      )}
    </div>
  );
}

function App() {
  return (
    <CometChatProvider
      appId="YOUR_APP_ID"
      region="YOUR_REGION"
      authKey="YOUR_AUTH_KEY"
    >
      <ChatWithSearch />
    </CometChatProvider>
  );
}

export default App;

Step 2: Create the search panel component

Build a MessageSearchPanel component that accepts a user or group, takes a search keyword, queries messages using MessagesRequestBuilder, and displays the results. File: MessageSearchPanel.tsx
import { useState, useCallback } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

interface MessageSearchPanelProps {
  user?: CometChat.User;
  group?: CometChat.Group;
  onMessageSelect: (message: CometChat.BaseMessage) => void;
  onClose: () => void;
}

function MessageSearchPanel({ user, group, onMessageSelect, onClose }: MessageSearchPanelProps) {
  const [keyword, setKeyword] = useState("");
  const [results, setResults] = useState<CometChat.BaseMessage[]>([]);
  const [loading, setLoading] = useState(false);

  const handleSearch = useCallback(async () => {
    if (!keyword.trim()) return;

    setLoading(true);
    try {
      const builder = new CometChat.MessagesRequestBuilder()
        .setSearchKeyword(keyword)
        .setLimit(50);

      if (user) {
        builder.setUID(user.getUid());
      } else if (group) {
        builder.setGUID(group.getGuid());
      }

      const request = builder.build();
      const messages = await request.fetchPrevious();
      setResults(messages);
    } catch (error) {
      console.error("Search failed:", error);
    } finally {
      setLoading(false);
    }
  }, [keyword, user, group]);

  return (
    <div style={{ width: "350px", borderLeft: "1px solid #e0e0e0", display: "flex", flexDirection: "column" }}>
      {/* Header */}
      <div style={{ padding: "12px 16px", borderBottom: "1px solid #e0e0e0", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <h3 style={{ margin: 0, fontSize: "16px" }}>Search Messages</h3>
        <button onClick={onClose} aria-label="Close search panel"></button>
      </div>

      {/* Search input */}
      <div style={{ padding: "12px 16px" }}>
        <input
          type="text"
          placeholder="Search messages..."
          value={keyword}
          onChange={(e) => setKeyword(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter") handleSearch(); }}
          style={{ width: "100%", padding: "8px 12px", borderRadius: "8px", border: "1px solid #e0e0e0" }}
          aria-label="Search keyword"
        />
      </div>

      {/* Results */}
      <div style={{ flex: 1, overflowY: "auto", padding: "0 16px" }}>
        {loading && <p>Searching...</p>}
        {!loading && results.length === 0 && keyword && <p>No messages found.</p>}
        {results.map((message) => (
          <button
            key={message.getId()}
            onClick={() => onMessageSelect(message)}
            style={{ display: "block", width: "100%", textAlign: "left", padding: "10px", marginBottom: "8px", border: "1px solid #e0e0e0", borderRadius: "8px", background: "none", cursor: "pointer" }}
          >
            <span style={{ fontSize: "12px", color: "#666" }}>
              {message.getSender()?.getName()}{" "}
              {new Date(message.getSentAt() * 1000).toLocaleDateString()}
            </span>
            <p style={{ margin: "4px 0 0", fontSize: "14px" }}>
              {(message as CometChat.TextMessage).getText?.() || "Media message"}
            </p>
          </button>
        ))}
      </div>
    </div>
  );
}

export default MessageSearchPanel;

Step 3: Navigate to a selected search result

When the user clicks a search result, pass the message ID to CometChatMessageList via the goToMessageId prop. This scrolls the message list to the matching message and highlights it. File: ChatWithSearch.tsx
const [goToMessageId, setGoToMessageId] = useState<number | undefined>(undefined);

function handleMessageSelect(message: CometChat.BaseMessage) {
  setGoToMessageId(message.getId());
  setShowSearch(false);
}

<CometChatMessageList
  user={user ?? undefined}
  group={group ?? undefined}
  goToMessageId={goToMessageId}
/>

Next Steps