Skip to main content
{
  "component": "CometChatMessageBubble",
  "package": "@cometchat/chat-uikit-react",
  "import": "import { CometChatMessageBubble } from \"@cometchat/chat-uikit-react\";",
  "cssImport": "import \"@cometchat/chat-uikit-react/css-variables.css\";",
  "description": "Base component for rendering message bubbles with customizable content, header, footer, and styling.",
  "cssRootClass": ".cometchat-message-bubble",
  "relatedComponents": {
    "CometChatTextBubble": "Text message content",
    "CometChatImageBubble": "Image message content",
    "CometChatVideoBubble": "Video message content",
    "CometChatAudioBubble": "Audio message content",
    "CometChatFileBubble": "File attachment content",
    "CometChatDocumentBubble": "Document preview content"
  },
  "props": {
    "data": {
      "id": { "type": "string", "required": true },
      "alignment": { "type": "MessageBubbleAlignment", "default": "left" }
    },
    "viewSlots": {
      "leadingView": "JSX.Element",
      "headerView": "JSX.Element",
      "contentView": "JSX.Element",
      "bottomView": "JSX.Element",
      "footerView": "JSX.Element",
      "statusInfoView": "JSX.Element",
      "threadView": "JSX.Element"
    }
  }
}

Overview

This guide shows you how to customize message bubbles using CometChat’s bubble components. You’ll learn to create custom layouts for different message types and build entirely custom message presentations. Time estimate: 15 minutes
Difficulty: Intermediate

Prerequisites

Steps

Step 1: Understand Bubble Architecture

Message bubbles have several customizable slots:
┌─────────────────────────────────────┐
│ leadingView │ headerView            │
├─────────────┼───────────────────────┤
│             │ contentView           │
│             ├───────────────────────┤
│             │ bottomView            │
├─────────────┴───────────────────────┤
│ footerView          │ statusInfoView│
├─────────────────────────────────────┤
│ threadView                          │
└─────────────────────────────────────┘

Step 2: Use Built-in Bubble Components

CometChat provides specialized bubble components for each message type:
import {
  CometChatTextBubble,
  CometChatImageBubble,
  CometChatVideoBubble,
  CometChatAudioBubble,
  CometChatFileBubble,
} from "@cometchat/chat-uikit-react";
import "@cometchat/chat-uikit-react/css-variables.css";

// Text message
function TextMessage({ text }: { text: string }) {
  return <CometChatTextBubble text={text} />;
}

// Image message
function ImageMessage({ url }: { url: string }) {
  return <CometChatImageBubble src={url} />;
}

// Video message
function VideoMessage({ url }: { url: string }) {
  return <CometChatVideoBubble src={url} />;
}

// Audio message
function AudioMessage({ url }: { url: string }) {
  return <CometChatAudioBubble src={url} />;
}

// File message
function FileMessage({ url, name }: { url: string; name: string }) {
  return <CometChatFileBubble fileUrl={url} fileName={name} />;
}

Step 3: Create Custom Message Bubble

Build a complete custom bubble:
import { CometChatMessageBubble, CometChatAvatar } from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

interface CustomBubbleProps {
  message: CometChat.TextMessage;
  alignment: "left" | "right";
}

function CustomMessageBubble({ message, alignment }: CustomBubbleProps) {
  const sender = message.getSender();
  
  return (
    <CometChatMessageBubble
      id={String(message.getId())}
      alignment={alignment}
      leadingView={
        alignment === "left" ? (
          <CometChatAvatar
            image={sender.getAvatar()}
            name={sender.getName()}
          />
        ) : undefined
      }
      headerView={
        <div className="bubble-header">
          <span className="sender-name">{sender.getName()}</span>
          <span className="timestamp">
            {new Date(message.getSentAt() * 1000).toLocaleTimeString()}
          </span>
        </div>
      }
      contentView={
        <div className="bubble-content">
          {message.getText()}
        </div>
      }
      footerView={
        <div className="bubble-footer">
          {message.getEditedAt() && <span>Edited</span>}
        </div>
      }
    />
  );
}

Step 4: Integrate with Message Templates

Use custom bubbles in message templates:
import {
  CometChatMessageList,
  CometChatMessageTemplate,
  CometChatMessageBubble,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

function ChatWithCustomBubbles({ user }: { user: CometChat.User }) {
  const customTextTemplate = new CometChatMessageTemplate({
    type: "text",
    category: "message",
    contentView: (message: CometChat.BaseMessage) => {
      const textMessage = message as CometChat.TextMessage;
      return (
        <div className="custom-text-content">
          <p>{textMessage.getText()}</p>
          <span className="word-count">
            {textMessage.getText().split(" ").length} words
          </span>
        </div>
      );
    },
  });

  return (
    <CometChatMessageList
      user={user}
      templates={[customTextTemplate]}
    />
  );
}

Complete Example

Full implementation with multiple custom bubble types:
import { useState, useEffect } from "react";
import {
  CometChatMessageList,
  CometChatMessageComposer,
  CometChatMessageTemplate,
  CometChatTextBubble,
  CometChatImageBubble,
  CometChatAvatar,
} from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import "@cometchat/chat-uikit-react/css-variables.css";

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

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

  // Custom text bubble with reactions preview
  const customTextTemplate = new CometChatMessageTemplate({
    type: "text",
    category: "message",
    contentView: (message: CometChat.BaseMessage) => {
      const textMessage = message as CometChat.TextMessage;
      return (
        <div className="enhanced-text-bubble">
          <CometChatTextBubble text={textMessage.getText()} />
          {textMessage.getReactions()?.length > 0 && (
            <div className="reactions-preview">
              {textMessage.getReactions().slice(0, 3).map((r, i) => (
                <span key={i}>{r.getReaction()}</span>
              ))}
            </div>
          )}
        </div>
      );
    },
  });

  // Custom image bubble with download button
  const customImageTemplate = new CometChatMessageTemplate({
    type: "image",
    category: "message",
    contentView: (message: CometChat.BaseMessage) => {
      const mediaMessage = message as CometChat.MediaMessage;
      const url = mediaMessage.getAttachment()?.getUrl() || "";
      
      return (
        <div className="enhanced-image-bubble">
          <CometChatImageBubble src={url} />
          <button 
            className="download-button"
            onClick={() => window.open(url, "_blank")}
          >
            ⬇️ Download
          </button>
        </div>
      );
    },
  });

  if (!user) return <div>Loading...</div>;

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
      <CometChatMessageList
        user={user}
        templates={[customTextTemplate, customImageTemplate]}
      />
      <CometChatMessageComposer user={user} />
    </div>
  );
}

export default ChatWithCustomBubbles;

Custom Bubble for Custom Messages

Handle custom message types:
import { CometChatMessageTemplate } from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

// Custom location message bubble
const locationTemplate = new CometChatMessageTemplate({
  type: "location",
  category: "custom",
  contentView: (message: CometChat.BaseMessage) => {
    const customMessage = message as CometChat.CustomMessage;
    const data = customMessage.getData()?.customData;
    
    return (
      <div className="location-bubble">
        <div className="location-map">
          <img 
            src={`https://maps.googleapis.com/maps/api/staticmap?center=${data.lat},${data.lng}&zoom=15&size=200x150&key=YOUR_API_KEY`}
            alt="Location"
          />
        </div>
        <div className="location-info">
          <strong>{data.name || "Shared Location"}</strong>
          <a 
            href={`https://maps.google.com/?q=${data.lat},${data.lng}`}
            target="_blank"
            rel="noopener noreferrer"
          >
            Open in Maps
          </a>
        </div>
      </div>
    );
  },
});

Bubble with Actions

Add interactive actions to bubbles:
import { CometChatMessageTemplate } from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

const interactiveTemplate = new CometChatMessageTemplate({
  type: "text",
  category: "message",
  contentView: (message: CometChat.BaseMessage) => {
    const textMessage = message as CometChat.TextMessage;
    
    return (
      <div className="interactive-bubble">
        <p>{textMessage.getText()}</p>
        <div className="bubble-actions">
          <button onClick={() => handleReply(message)}>Reply</button>
          <button onClick={() => handleForward(message)}>Forward</button>
          <button onClick={() => handleCopy(textMessage.getText())}>Copy</button>
        </div>
      </div>
    );
  },
});

function handleReply(message: CometChat.BaseMessage) {
  console.log("Reply to:", message.getId());
}

function handleForward(message: CometChat.BaseMessage) {
  console.log("Forward:", message.getId());
}

function handleCopy(text: string) {
  navigator.clipboard.writeText(text);
}

Animated Bubbles

Add animations to message bubbles:
import { CometChatMessageTemplate } from "@cometchat/chat-uikit-react";
import { CometChat } from "@cometchat/chat-sdk-javascript";

const animatedTemplate = new CometChatMessageTemplate({
  type: "text",
  category: "message",
  contentView: (message: CometChat.BaseMessage) => {
    const textMessage = message as CometChat.TextMessage;
    
    return (
      <div 
        className="animated-bubble"
        style={{
          animation: "slideIn 0.3s ease-out",
        }}
      >
        {textMessage.getText()}
      </div>
    );
  },
});

Styling

Customize bubble appearance:
/* Base bubble styles */
.cometchat-message-bubble {
  margin: 8px 16px;
  max-width: 70%;
}

.cometchat-message-bubble--left {
  margin-right: auto;
}

.cometchat-message-bubble--right {
  margin-left: auto;
}

/* Content area */
.cometchat-message-bubble__content {
  padding: 12px 16px;
  border-radius: 16px;
  background: #f5f5f5;
}

.cometchat-message-bubble--right .cometchat-message-bubble__content {
  background: #6852d6;
  color: #ffffff;
}

/* Header */
.bubble-header {
  display: flex;
  justify-content: space-between;
  margin-bottom: 4px;
  font-size: 12px;
}

.sender-name {
  font-weight: 600;
}

.timestamp {
  color: #999;
}

/* Custom enhancements */
.enhanced-text-bubble {
  position: relative;
}

.reactions-preview {
  position: absolute;
  bottom: -8px;
  right: 8px;
  background: #ffffff;
  border-radius: 12px;
  padding: 2px 6px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.enhanced-image-bubble {
  position: relative;
}

.download-button {
  position: absolute;
  bottom: 8px;
  right: 8px;
  padding: 4px 8px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.6);
  color: #ffffff;
  border: none;
  cursor: pointer;
}

/* Animation */
@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Common Issues

When customizing bubbles, ensure you handle all message states including loading, error, and deleted messages.
IssueSolution
Bubble not renderingCheck message type matches template type
Styling conflictsUse specific CSS selectors with component classes
Missing contentEnsure contentView returns valid JSX
Performance issuesAvoid heavy computations in render functions

Next Steps