Skip to main content
Quick Reference
// Listen for real-time messages (register AFTER login)
CometChat.addMessageListener("LISTENER_ID", new CometChat.MessageListener({
  onTextMessageReceived: (msg) => console.log(msg.getText()),
  onMediaMessageReceived: (msg) => console.log(msg.getAttachment()),
}));

// Fetch message history
const request = new CometChat.MessagesRequestBuilder().setUID("USER_UID").setLimit(30).build();
const messages = await request.fetchPrevious();

// Cleanup (on component unmount)
CometChat.removeMessageListener("LISTENER_ID");
Key Points:
  • Register listeners AFTER successful login
  • Use unique listener IDs (duplicates overwrite)
  • Always remove listeners on cleanup
Receiving messages in CometChat involves two parts. If you’re unfamiliar with message categories (message, custom, action, call) and types (text, image, video, etc.), review the Message Structure & Hierarchy guide first.
Available via: SDK | REST API | UI Kits
  1. Real-time messages - Listen for new messages while your app is running
  2. Message history - Fetch previous messages when loading a conversation

Real-Time Messages

Register a MessageListener to receive messages instantly:
const listenerID = "UNIQUE_LISTENER_ID";

CometChat.addMessageListener(
  listenerID,
  new CometChat.MessageListener({
    onTextMessageReceived: (textMessage) => {
      console.log("Text message received:", textMessage);
    },
    onMediaMessageReceived: (mediaMessage) => {
      console.log("Media message received:", mediaMessage);
    },
    onCustomMessageReceived: (customMessage) => {
      console.log("Custom message received:", customMessage);
    }
  })
);
As a sender, you won’t receive your own messages in real-time events. However, if logged in on multiple devices, other devices will receive the event.

Remove Listener

Always remove listeners when they’re no longer needed (e.g., when unmounting a component):
CometChat.removeMessageListener(listenerID);

All Message Events

CometChat.addMessageListener(
  listenerID,
  new CometChat.MessageListener({
    // Message received
    onTextMessageReceived: (textMessage) => {},
    onMediaMessageReceived: (mediaMessage) => {},
    onCustomMessageReceived: (customMessage) => {},
    onInteractiveMessageReceived: (interactiveMessage) => {},
    
    // Message updates
    onMessageEdited: (message) => {},
    onMessageDeleted: (message) => {},
    
    // Typing indicators
    onTypingStarted: (typingIndicator) => {},
    onTypingEnded: (typingIndicator) => {},
    
    // Read receipts
    onMessagesDelivered: (messageReceipt) => {},
    onMessagesRead: (messageReceipt) => {},
    onMessagesDeliveredToAll: (messageReceipt) => {},  // Groups only
    onMessagesReadByAll: (messageReceipt) => {},       // Groups only
    
    // Transient messages
    onTransientMessageReceived: (transientMessage) => {}
  })
);

Message History

Fetch previous messages for a conversation using MessagesRequest.

One-on-One Conversation

const UID = "user_id";
const limit = 30;

const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setLimit(limit)
  .build();

messagesRequest.fetchPrevious().then(
  (messages) => {
    console.log("Messages:", messages);
  },
  (error) => {
    console.log("Error:", error);
  }
);

Group Conversation

const GUID = "group_id";
const limit = 30;

const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setGUID(GUID)
  .setLimit(limit)
  .build();

messagesRequest.fetchPrevious().then(
  (messages) => console.log("Messages:", messages),
  (error) => console.log("Error:", error)
);

Pagination

Call fetchPrevious() repeatedly on the same request object to load older messages:
// Initial load
const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setLimit(30)
  .build();

// First page
messagesRequest.fetchPrevious().then((messages) => {
  displayMessages(messages);
});

// Load more (on scroll up)
function loadMoreMessages() {
  messagesRequest.fetchPrevious().then((messages) => {
    prependMessages(messages);
  });
}

Missed Messages

Fetch messages that arrived while your app was offline.
const UID = "user_id";
const limit = 30;
const latestId = await CometChat.getLastDeliveredMessageId();

const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setMessageId(latestId)
  .setLimit(limit)
  .build();

messagesRequest.fetchNext().then(
  (messages) => console.log("Missed messages:", messages),
  (error) => console.log("Error:", error)
);
Use fetchNext() for missed messages (messages after a specific ID) and fetchPrevious() for message history (messages before current view).

Unread Messages

Fetch only unread messages:
const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setUnread(true)
  .setLimit(30)
  .build();

messagesRequest.fetchPrevious().then(
  (messages) => console.log("Unread messages:", messages),
  (error) => console.log("Error:", error)
);

Search Messages

Search for messages containing specific text:
const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setSearchKeyword("hello")
  .setLimit(30)
  .build();

messagesRequest.fetchPrevious().then(
  (messages) => console.log("Search results:", messages),
  (error) => console.log("Error:", error)
);
Advanced Search (available in Advanced & Custom plans) searches across message text, file names, mentions, and mime types. Enable it in Dashboard → Chats → Settings → General Configuration.
FeatureBasic SearchAdvanced Search
Text search
File name search
Mentions search
Mime type search

Unread Message Count

For a Specific User

CometChat.getUnreadMessageCountForUser(UID).then(
  (count) => console.log("Unread from user:", count),
  (error) => console.log("Error:", error)
);

For a Specific Group

CometChat.getUnreadMessageCountForGroup(GUID).then(
  (count) => console.log("Unread in group:", count),
  (error) => console.log("Error:", error)
);

For All Conversations

CometChat.getUnreadMessageCount().then(
  (count) => {
    console.log("Users:", count.users);   // { uid1: 5, uid2: 3 }
    console.log("Groups:", count.groups); // { guid1: 10 }
  },
  (error) => console.log("Error:", error)
);

Exclude Blocked Users

const hideBlockedUsers = true;

CometChat.getUnreadMessageCount(hideBlockedUsers).then(
  (count) => console.log("Unread count:", count)
);

Filter Options

The MessagesRequestBuilder supports various filters:
MethodDescription
setUID(uid)Filter by user conversation
setGUID(guid)Filter by group conversation
setLimit(limit)Number of messages to fetch (max 100)
setMessageId(id)Fetch messages around this ID
setTimestamp(timestamp)Fetch messages around this time
setUnread(true)Only unread messages
setSearchKeyword(keyword)Search in message content
setCategory(category)Filter by category (message, action, call, custom)
setType(type)Filter by type (text, image, video, etc.)
setTypes(types)Filter by multiple types
setCategories(categories)Filter by multiple categories
hideReplies(true)Exclude threaded replies
hideDeletedMessages(true)Exclude deleted messages
setTags(tags)Filter by message tags
withTags(true)Include tags in response
setMentionsWithTagInfo(true)Include mention details
setMentionsWithBlockedInfo(true)Include blocked user info in mentions

Example: Filter by Type

const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setLimit(30)
  .setTypes(["image", "video"])
  .setCategories(["message"])
  .build();

Example: Exclude Replies and Deleted

const messagesRequest = new CometChat.MessagesRequestBuilder()
  .setUID(UID)
  .setLimit(30)
  .hideReplies(true)
  .hideDeletedMessages(true)
  .build();

Message Object

Messages are returned as BaseMessage objects. Use instanceof to determine the type:
messages.forEach((message) => {
  if (message instanceof CometChat.TextMessage) {
    console.log("Text:", message.getText());
  } else if (message instanceof CometChat.MediaMessage) {
    console.log("Media URL:", message.getAttachment()?.getUrl());
  } else if (message instanceof CometChat.CustomMessage) {
    console.log("Custom data:", message.getCustomData());
  } else if (message instanceof CometChat.Action) {
    console.log("Action:", message.getAction());
  }
});

Common Properties

PropertyMethodDescription
IDgetId()Unique message identifier
SendergetSender()User object of sender
ReceivergetReceiver()User/Group object
Sent AtgetSentAt()Unix timestamp
TypegetType()text, image, video, etc.
CategorygetCategory()message, action, call, custom
Read AtgetReadAt()When message was read
Delivered AtgetDeliveredAt()When message was delivered
Edited AtgetEditedAt()When message was edited
Deleted AtgetDeletedAt()When message was deleted

React Implementation Example

Here’s a complete React hook for handling messages:
import { useEffect, useState, useCallback, useRef } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

export function useMessages(conversationId, conversationType = "user") {
  const [messages, setMessages] = useState([]);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const requestRef = useRef(null);

  // Initialize messages request
  useEffect(() => {
    const builder = new CometChat.MessagesRequestBuilder()
      .setLimit(30);

    if (conversationType === "user") {
      builder.setUID(conversationId);
    } else {
      builder.setGUID(conversationId);
    }

    requestRef.current = builder.build();
    setMessages([]);
    setHasMore(true);

    // Load initial messages
    loadMessages();

    // Setup real-time listener
    const listenerID = `messages_${conversationId}`;
    CometChat.addMessageListener(
      listenerID,
      new CometChat.MessageListener({
        onTextMessageReceived: (message) => {
          if (isRelevantMessage(message)) {
            setMessages((prev) => [...prev, message]);
            CometChat.markAsRead(message);
          }
        },
        onMediaMessageReceived: (message) => {
          if (isRelevantMessage(message)) {
            setMessages((prev) => [...prev, message]);
            CometChat.markAsRead(message);
          }
        },
        onMessageEdited: (message) => {
          setMessages((prev) =>
            prev.map((m) => (m.getId() === message.getId() ? message : m))
          );
        },
        onMessageDeleted: (message) => {
          setMessages((prev) =>
            prev.map((m) => (m.getId() === message.getId() ? message : m))
          );
        }
      })
    );

    return () => {
      CometChat.removeMessageListener(listenerID);
    };
  }, [conversationId, conversationType]);

  const isRelevantMessage = (message) => {
    const sender = message.getSender().getUid();
    const receiver = message.getReceiverId();
    return sender === conversationId || receiver === conversationId;
  };

  const loadMessages = useCallback(async () => {
    if (!requestRef.current || loading || !hasMore) return;

    setLoading(true);
    try {
      const fetchedMessages = await requestRef.current.fetchPrevious();
      setMessages((prev) => [...fetchedMessages, ...prev]);
      setHasMore(fetchedMessages.length === 30);

      // Mark last message as read
      if (fetchedMessages.length > 0) {
        CometChat.markAsRead(fetchedMessages[fetchedMessages.length - 1]);
      }
    } catch (error) {
      console.error("Failed to load messages:", error);
    } finally {
      setLoading(false);
    }
  }, [loading, hasMore]);

  const sendMessage = useCallback(async (text) => {
    const receiverType = conversationType === "user"
      ? CometChat.RECEIVER_TYPE.USER
      : CometChat.RECEIVER_TYPE.GROUP;

    const textMessage = new CometChat.TextMessage(
      conversationId,
      text,
      receiverType
    );

    const sentMessage = await CometChat.sendMessage(textMessage);
    setMessages((prev) => [...prev, sentMessage]);
    return sentMessage;
  }, [conversationId, conversationType]);

  return {
    messages,
    loading,
    hasMore,
    loadMore: loadMessages,
    sendMessage
  };
}

// Usage in component
function ChatScreen({ conversationId }) {
  const { messages, loading, hasMore, loadMore, sendMessage } = useMessages(conversationId);
  const [input, setInput] = useState("");

  const handleSend = async () => {
    if (!input.trim()) return;
    await sendMessage(input);
    setInput("");
  };

  return (
    <div>
      {hasMore && <button onClick={loadMore}>Load More</button>}
      {loading && <div>Loading...</div>}
      {messages.map((msg) => (
        <div key={msg.getId()}>{msg.getText?.() || "Media message"}</div>
      ))}
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={handleSend}>Send</button>
    </div>
  );
}

Next Steps