Skip to main content
{
  "component": "CometChatMessageTemplate",
  "kind": "model-class",
  "package": "@cometchat/chat-uikit-react",
  "import": "import { CometChatMessageTemplate } from \"@cometchat/chat-uikit-react\";",
  "description": "Data structure defining how message bubbles render in CometChatMessageList. Each template maps a type+category pair to view functions.",
  "usage": "Pass an array of CometChatMessageTemplate instances to CometChatMessageList via the templates prop.",
  "properties": {
    "type": { "type": "string", "required": true, "description": "CometChat message type" },
    "category": { "type": "string", "default": "\"\"", "description": "CometChat message category" },
    "headerView": { "type": "function | null", "default": "null", "description": "Custom header view function" },
    "contentView": { "type": "function | null", "default": "null", "description": "Custom content view function" },
    "footerView": { "type": "function | null", "default": "null", "description": "Custom footer view function" },
    "bottomView": { "type": "function | null", "default": "null", "description": "Custom bottom view function" },
    "bubbleView": { "type": "function | null", "default": "null", "description": "Replaces entire bubble" },
    "statusInfoView": { "type": "function | null", "default": "null", "description": "Custom status info view function" },
    "replyView": { "type": "function | null", "default": "null", "description": "Custom reply preview function" },
    "options": { "type": "function", "description": "Returns action sheet items for long-press" }
  },
  "relatedComponents": ["CometChatMessageList"]
}

Overview

Message templates let you customize how different message types render in your chat. You can modify individual parts of a message bubble (header, content, footer) or replace the entire bubble for specific message types. Time estimate: 20 minutes
Difficulty: Intermediate

Prerequisites

Steps

Step 1: Understand the bubble structure

A message bubble has these customizable slots:
ViewDefaultPurpose
headerViewSender nameTop of bubble
contentViewMessage contentMain body
footerViewReactionsBelow content
bottomViewLink previewsBottom section
statusInfoViewReceipt + timestampStatus area
replyViewReply previewReply context
bubbleViewEntire bubbleReplaces all above

Step 2: Get existing templates

Retrieve the built-in templates and modify specific ones:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatMessageTemplate,
  CometChatUIKit,
  CometChatUIKitConstants,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function CustomTemplateDemo() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("guid").then((group) => setChatGroup(group));

    // Get all built-in templates
    const allTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    setTemplates(allTemplates);
  }, []);

  if (!chatGroup) return null;

  return <CometChatMessageList group={chatGroup} templates={templates} />;
}

Step 3: Customize the header view

Add a status badge to the sender name:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatUIKit,
  CometChatUIKitConstants,
  CometChatMessageTemplate,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function HeaderViewDemo() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("guid").then((group) => setChatGroup(group));

    const definedTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    const modified = definedTemplates.map((t) => {
      if (
        t.type === CometChatUIKitConstants.MessageTypes.text &&
        t.category === CometChatUIKitConstants.MessageCategory.message
      ) {
        t.headerView = (message: CometChat.BaseMessage) => (
          <>{message.getSender().getName()} • 🗓️ In meeting</>
        );
      }
      return t;
    });
    setTemplates(modified);
  }, []);

  if (!chatGroup) return null;

  return <CometChatMessageList group={chatGroup} templates={templates} />;
}
Custom header view with status badge

Step 4: Create a custom content view

Build a product card for custom message types:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatUIKit,
  CometChatUIKitConstants,
  CometChatMessageTemplate,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function ContentViewDemo() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("guid").then((group) => setChatGroup(group));

    const definedTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    const CUSTOM_MESSAGE_TYPE = "product";
    
    const customTemplate = new CometChatMessageTemplate({
      type: CUSTOM_MESSAGE_TYPE,
      category: CometChatUIKitConstants.MessageCategory.custom,
      contentView: (message: CometChat.BaseMessage) => (
        <div className="product-card">
          <div className="product-card__body">
            <div className="product-card__title">Blazer Casual</div>
            <div className="product-card__description">
              Men's Tailored Regular Fit Blazer
            </div>
            <div className="product-card__price">
              $37.99 <span className="product-card__price-old">$74.00</span>
            </div>
          </div>
          <div className="product-card__footer">Buy now</div>
        </div>
      ),
    });
    
    definedTemplates.push(customTemplate);
    setTemplates(definedTemplates);
  }, []);

  const getMessageRequestBuilder = () => {
    const CUSTOM_MESSAGE_TYPE = "product";
    const categories = CometChatUIKit.getDataSource().getAllMessageCategories();
    categories.push(CometChatUIKitConstants.MessageCategory.custom);
    const types = CometChatUIKit.getDataSource().getAllMessageTypes();
    types.push(CUSTOM_MESSAGE_TYPE);
    
    return new CometChat.MessagesRequestBuilder()
      .setCategories(categories)
      .setTypes(types)
      .hideReplies(true)
      .setLimit(30);
  };

  if (!chatGroup) return null;

  return (
    <CometChatMessageList
      group={chatGroup}
      templates={templates}
      messagesRequestBuilder={getMessageRequestBuilder()}
    />
  );
}
Custom product card content view

Step 5: Replace the entire bubble

For complete control, use bubbleView to replace the whole message:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatUIKit,
  CometChatUIKitConstants,
  CometChatMessageTemplate,
  MessageBubbleAlignment,
  CometChatUIKitLoginListener,
  isMessageSentByMe,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function BubbleViewDemo() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("guid").then((group) => setChatGroup(group));

    const definedTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    const modified = definedTemplates.map((t) => {
      if (
        t.type === CometChatUIKitConstants.MessageTypes.text &&
        t.category === CometChatUIKitConstants.MessageCategory.message
      ) {
        t.bubbleView = (
          message: CometChat.BaseMessage,
          alignment: MessageBubbleAlignment
        ) => {
          const isSentByMe = isMessageSentByMe(
            message,
            CometChatUIKitLoginListener.getLoggedInUser()!
          );
          let textMessage = "";
          if (message instanceof CometChat.TextMessage) {
            textMessage = message.getText();
          }
          return (
            <div className={`custom-bubble ${isSentByMe ? "outgoing" : "incoming"}`}>
              <div className="custom-bubble__text">{textMessage}</div>
            </div>
          );
        };
      }
      return t;
    });
    setTemplates(modified);
  }, []);

  if (!chatGroup) return null;

  return <CometChatMessageList group={chatGroup} templates={templates} />;
}
Custom bubble view

Step 6: Customize action sheet options

Add custom actions to the message long-press menu:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatUIKit,
  CometChatUIKitConstants,
  CometChatMessageTemplate,
  CometChatActionsIcon,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function OptionsDemo() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("guid").then((group) => setChatGroup(group));

    const definedTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    const modified = definedTemplates.map((t) => {
      if (
        t.type === CometChatUIKitConstants.MessageTypes.text &&
        t.category === CometChatUIKitConstants.MessageCategory.message
      ) {
        t.options = (
          loggedInUser: CometChat.User,
          message: CometChat.BaseMessage,
          group?: CometChat.Group
        ) => {
          const defaultOptions: any =
            CometChatUIKit.getDataSource().getMessageOptions(loggedInUser, message, group);
          
          const customAction: any = new CometChatActionsIcon({
            id: "bookmark",
            title: "Bookmark",
            iconURL: "",
            onClick: () => console.log("Bookmarked:", message.getId()),
          });
          
          defaultOptions.splice(1, 0, customAction);
          return defaultOptions;
        };
      }
      return t;
    });
    setTemplates(modified);
  }, []);

  if (!chatGroup) return null;

  return <CometChatMessageList group={chatGroup} templates={templates} />;
}
Custom action sheet options

Complete Example

import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatMessageTemplate,
  CometChatUIKit,
  CometChatUIKitConstants,
  MessageBubbleAlignment,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

function CustomizedMessageList() {
  const [chatGroup, setChatGroup] = useState<CometChat.Group>();
  const [templates, setTemplates] = useState<CometChatMessageTemplate[]>([]);

  useEffect(() => {
    CometChat.getGroup("YOUR_GROUP_GUID").then((group) => setChatGroup(group));

    const allTemplates = CometChatUIKit.getDataSource().getAllMessageTemplates();
    
    // Customize text messages
    const modified = allTemplates.map((t) => {
      if (
        t.type === CometChatUIKitConstants.MessageTypes.text &&
        t.category === CometChatUIKitConstants.MessageCategory.message
      ) {
        // Add status to header
        t.headerView = (message: CometChat.BaseMessage) => (
          <span style={{ fontSize: 12, color: "#666" }}>
            {message.getSender().getName()} • Online
          </span>
        );
        
        // Add warning to bottom
        t.bottomView = (message: CometChat.BaseMessage) => (
          <div style={{ fontSize: 11, color: "#f44649", padding: "4px 8px" }}>
            ⚠️ Message expires in 24 hours
          </div>
        );
      }
      return t;
    });
    
    setTemplates(modified);
  }, []);

  if (!chatGroup) return null;

  return (
    <div style={{ height: "100vh" }}>
      <CometChatMessageList group={chatGroup} templates={templates} />
    </div>
  );
}

export default CustomizedMessageList;
Create a template for a completely new message type:
const CUSTOM_TYPE = "location";

const locationTemplate = new CometChatMessageTemplate({
  type: CUSTOM_TYPE,
  category: CometChatUIKitConstants.MessageCategory.custom,
  contentView: (message: CometChat.BaseMessage) => {
    const data = message.getData() as { lat: number; lng: number };
    return (
      <div style={{ padding: 12 }}>
        <img 
          src={`https://maps.googleapis.com/maps/api/staticmap?center=${data.lat},${data.lng}&zoom=14&size=200x150&key=YOUR_KEY`}
          alt="Location"
        />
        <div>📍 Shared location</div>
      </div>
    );
  },
});

// Add to templates array
allTemplates.push(locationTemplate);

Common Issues

When using bubbleView, all other view slots (headerView, contentView, etc.) are ignored for that template.
IssueSolution
Custom template not showingEnsure type and category match exactly
Custom messages not loadingAdd type to MessagesRequestBuilder
Styles not applyingCheck CSS specificity, use .cometchat prefix

Next Steps