Skip to main content
FieldValue
Package@cometchat/chat-uikit-react
ComponentCometChatMessageComposer — rich text input with attachments
CSS Import@import url("@cometchat/chat-uikit-react/css-variables.css");
Key Propsuser, group, attachmentOptions, textFormatters
EventsccMessageSent, ccMessageEdited, ccReplyToMessage

Overview

This guide shows you how to build a rich message input with attachments, emoji, voice recording, mentions, and custom actions. You’ll learn to customize the composer for different use cases. Time estimate: 15 minutes Difficulty: Intermediate

Prerequisites

Use Cases

The message composer is essential for:
  • Text messaging — Send plain text and formatted messages
  • Media sharing — Attach images, videos, audio, and files
  • Voice notes — Record and send voice messages
  • Mentions — Tag users with @mentions
  • Custom inputs — Add polls, locations, or custom message types

Steps

Step 1: Add Basic Composer

Add the composer to your chat view:
BasicComposer.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function BasicComposer() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  return <CometChatMessageComposer user={user} />;
}

export default BasicComposer;
Message composer with all features

Step 2: Create a Minimal Text-Only Composer

Hide all attachment options for a simple text input:
MinimalComposer.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function MinimalComposer() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  return (
    <CometChatMessageComposer
      user={user}
      hideAttachmentButton={true}
      hideVoiceRecordingButton={true}
      hideEmojiKeyboardButton={true}
      hideStickersButton={true}
      placeholderText="Type a message..."
    />
  );
}

export default MinimalComposer;

Step 3: Configure Enter Key Behavior

Change how the Enter key works:
EnterKeyComposer.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
  CometChatMessageComposer,
  EnterKeyBehavior,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function EnterKeyComposer() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  return (
    <CometChatMessageComposer
      user={user}
      // Options: SendMessage (default), NewLine, None
      enterKeyBehavior={EnterKeyBehavior.NewLine}
    />
  );
}

export default EnterKeyComposer;
BehaviorDescription
SendMessageEnter sends the message (default)
NewLineEnter adds a new line, Shift+Enter sends
NoneEnter does nothing, must click send button

Step 4: Add Custom Attachment Options

Replace or extend the default attachment options:
CustomAttachments.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
  CometChatMessageComposer,
  CometChatMessageComposerAction,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function CustomAttachments() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  const customAttachments = [
    new CometChatMessageComposerAction({
      id: "location",
      title: "Share Location",
      iconURL: "/icons/location.svg",
      onClick: () => {
        console.log("Share location clicked");
        // Implement location sharing
      },
    }),
    new CometChatMessageComposerAction({
      id: "contact",
      title: "Share Contact",
      iconURL: "/icons/contact.svg",
      onClick: () => {
        console.log("Share contact clicked");
        // Implement contact sharing
      },
    }),
    new CometChatMessageComposerAction({
      id: "poll",
      title: "Create Poll",
      iconURL: "/icons/poll.svg",
      onClick: () => {
        console.log("Create poll clicked");
        // Implement poll creation
      },
    }),
  ];

  return (
    <CometChatMessageComposer
      user={user}
      attachmentOptions={customAttachments}
    />
  );
}

export default CustomAttachments;
Custom attachment options

Step 5: Handle Send Events

Intercept the send action for custom handling:
CustomSendHandler.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function CustomSendHandler() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  return (
    <CometChatMessageComposer
      user={user}
      onSendButtonClick={(message) => {
        console.log("Message to send:", message);
        // Add custom logic before sending
        // The message is still sent automatically
      }}
      onTextChange={(text) => {
        console.log("Text changed:", text);
        // Track typing, validate input, etc.
      }}
      onError={(error) => {
        console.error("Composer error:", error);
      }}
    />
  );
}

export default CustomSendHandler;

Step 6: Add a Header View

Display information above the composer:
ComposerWithHeader.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function ComposerWithHeader() {
  const [user, setUser] = useState<CometChat.User>();

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

  if (!user) return null;

  return (
    <CometChatMessageComposer
      user={user}
      headerView={
        <div style={{
          padding: "8px 16px",
          background: "#fff3cd",
          borderRadius: "8px",
          display: "flex",
          alignItems: "center",
          gap: "8px",
        }}>
          <span>⚠️</span>
          <span>User has paused notifications</span>
        </div>
      }
    />
  );
}

export default ComposerWithHeader;
Composer with header view

Step 7: Configure Mentions

Customize how @mentions work:
MentionsComposer.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function MentionsComposer() {
  const [group, setGroup] = useState<CometChat.Group>();

  useEffect(() => {
    CometChat.getGroup("group_guid").then(setGroup);
  }, []);

  if (!group) return null;

  return (
    <CometChatMessageComposer
      group={group}
      // Disable @all mentions
      disableMentionAll={false}
      // Custom label for @all (default is "all")
      mentionAllLabel="everyone"
      // Custom request builder for mention suggestions
      mentionsGroupMembersRequestBuilder={
        new CometChat.GroupMembersRequestBuilder(group.getGuid())
          .setLimit(10)
          .setSearchKeyword("")
      }
    />
  );
}

export default MentionsComposer;

Step 8: Thread Replies

Create a composer for threaded conversations:
ThreadComposer.tsx
import { useState, useEffect } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatMessageComposer } from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

function ThreadComposer({ parentMessage }: { parentMessage: CometChat.BaseMessage }) {
  const [user, setUser] = useState<CometChat.User>();

  useEffect(() => {
    const sender = parentMessage.getSender();
    if (sender) {
      CometChat.getUser(sender.getUid()).then(setUser);
    }
  }, [parentMessage]);

  if (!user) return null;

  return (
    <CometChatMessageComposer
      user={user}
      parentMessageId={parentMessage.getId()}
      placeholderText="Reply in thread..."
    />
  );
}

export default ThreadComposer;

Complete Example

Here’s a full chat view with a customized composer:
ChatWithComposer.tsx
import { useState, useEffect } from "react";
import {
  CometChatConversations,
  CometChatMessageHeader,
  CometChatMessageList,
  CometChatMessageComposer,
  CometChatMessageComposerAction,
  CometChatMessageEvents,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

function ChatWithComposer() {
  const [selectedConversation, setSelectedConversation] = useState<CometChat.Conversation>();

  // Listen to message events
  useEffect(() => {
    const sentSub = CometChatMessageEvents.ccMessageSent.subscribe((data) => {
      console.log("Message sent:", data);
    });

    return () => {
      sentSub?.unsubscribe();
    };
  }, []);

  const getConversationWith = () => {
    if (!selectedConversation) return null;
    const type = selectedConversation.getConversationType();
    if (type === "user") {
      return { user: selectedConversation.getConversationWith() as CometChat.User };
    }
    return { group: selectedConversation.getConversationWith() as CometChat.Group };
  };

  const conversationWith = getConversationWith();

  // Custom attachment options
  const attachmentOptions = [
    new CometChatMessageComposerAction({
      id: "image",
      title: "Photo",
      iconURL: "/icons/image.svg",
    }),
    new CometChatMessageComposerAction({
      id: "file",
      title: "Document",
      iconURL: "/icons/file.svg",
    }),
  ];

  return (
    <div style={{ display: "flex", height: "100vh", width: "100vw" }}>
      <div style={{ width: 400, borderRight: "1px solid #eee" }}>
        <CometChatConversations
          onItemClick={(conversation) => setSelectedConversation(conversation)}
        />
      </div>

      {conversationWith ? (
        <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
          <CometChatMessageHeader {...conversationWith} />
          <CometChatMessageList {...conversationWith} />
          <CometChatMessageComposer
            {...conversationWith}
            placeholderText="Type a message..."
            hideCollaborativeDocumentOption={true}
            hideCollaborativeWhiteboardOption={true}
            hidePollsOption={true}
            onSendButtonClick={(message) => {
              console.log("Sending:", message);
            }}
          />
        </div>
      ) : (
        <div style={{ flex: 1, display: "grid", placeItems: "center", color: "#727272" }}>
          Select a conversation
        </div>
      )}
    </div>
  );
}

export default ChatWithComposer;
PropDescription
hideAttachmentButtonHide entire attachment button
hideImageAttachmentOptionHide image option
hideVideoAttachmentOptionHide video option
hideAudioAttachmentOptionHide audio option
hideFileAttachmentOptionHide file option
hidePollsOptionHide polls option
hideCollaborativeDocumentOptionHide collaborative document
hideCollaborativeWhiteboardOptionHide collaborative whiteboard
hideVoiceRecordingButtonHide voice recording
hideEmojiKeyboardButtonHide emoji keyboard
hideStickersButtonHide stickers
hideSendButtonHide send button

Key CSS Selectors

TargetSelector
Root.cometchat-message-composer
Send button.cometchat-message-composer__send-button
Send button (active).cometchat-message-composer__send-button-active
Attachment popover.cometchat-message-composer__secondary-button-view-attachment-button .cometchat-popover__content
Emoji popover.cometchat-message-composer__emoji-keyboard-button .cometchat-popover__content
Header area.cometchat-message-composer__header

Example: Brand-Themed Composer

.cometchat-message-composer__send-button .cometchat-button {
  background: #6852d6;
  border-radius: 50%;
}

.cometchat-message-composer__send-button .cometchat-button__icon {
  background: white;
}

Common Issues

The composer requires either a user or group prop. Without one, it won’t know where to send messages.
IssueSolution
Messages not sendingEnsure user or group prop is set
Attachments not workingCheck file permissions and size limits
Mentions not appearingVerify group membership for group chats
Voice recording failsCheck browser microphone permissions
Custom attachments not showingEnsure attachmentOptions array is valid
For more help, see the Troubleshooting Guide or contact CometChat Support.

Next Steps