> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-llms-scoped-indexes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Message List

> Scrollable message feed with plugin-based bubble rendering, reactions, receipts, threads, and real-time updates.

<Accordion title="AI Integration Quick Reference">
  ```json theme={null}
  {
    "component": "CometChatMessageList",
    "package": "@cometchat/chat-uikit-react",
    "import": "import { CometChatMessageList } from \"@cometchat/chat-uikit-react\";",
    "description": "Scrollable message feed with plugin-based bubble rendering, reactions, receipts, threads, and real-time updates.",
    "cssRootClass": ".cometchat-message-list",
    "primaryOutput": {
      "prop": "onThreadRepliesClick",
      "type": "(message: CometChat.BaseMessage) => void"
    },
    "props": {
      "entity": {
        "user": {
          "type": "CometChat.User",
          "default": "undefined",
          "note": "For 1-on-1 chat. Mutually exclusive with group."
        },
        "group": {
          "type": "CometChat.Group",
          "default": "undefined",
          "note": "For group chat. Mutually exclusive with user."
        },
        "parentMessage": {
          "type": "CometChat.BaseMessage",
          "default": "undefined",
          "note": "Enables thread mode — fetches replies to this message. Preferred over parentMessageId; holding the whole parent also lets the list reflect live thread-subscription state on realtime replies."
        },
        "parentMessageId": {
          "type": "number",
          "default": "undefined",
          "note": "Deprecated — pass parentMessage instead. Still honoured (as the parent's id) when parentMessage is absent."
        }
      },
      "data": {
        "messagesRequestBuilder": {
          "type": "CometChat.MessagesRequestBuilder",
          "default": "SDK default (limit 30)",
          "note": "Pass the builder instance, not the result of .build()"
        },
        "reactionsRequestBuilder": {
          "type": "CometChat.ReactionsRequestBuilder",
          "default": "undefined"
        },
        "messageTypes": {
          "type": "string[]",
          "default": "Plugin registry types"
        },
        "messageCategories": {
          "type": "string[]",
          "default": "Plugin registry categories"
        },
        "goToMessageId": {
          "type": "number",
          "default": "undefined",
          "note": "Jump to a specific message (e.g., from search or deep link)"
        },
        "startFromUnreadMessages": {
          "type": "boolean",
          "default": false
        },
        "loadLastAgentConversation": {
          "type": "boolean",
          "default": false
        }
      },
      "visibility": {
        "hideReceipts": { "type": "boolean", "default": false },
        "hideStickyDate": { "type": "boolean", "default": false },
        "hideDateSeparator": { "type": "boolean", "default": false },
        "hideAvatar": { "type": "boolean", "default": false },
        "hideGroupActionMessages": { "type": "boolean", "default": false },
        "hideModerationView": { "type": "boolean", "default": false },
        "showScrollbar": { "type": "boolean", "default": false },
        "showSmartReplies": { "type": "boolean", "default": false },
        "showConversationStarters": { "type": "boolean", "default": false },
        "showMarkAsUnreadOption": { "type": "boolean", "default": false },
        "disableTruncation": { "type": "boolean", "default": false },
        "isAgentChat": { "type": "boolean", "default": false }
      },
      "optionToggles": {
        "hideReplyOption": { "type": "boolean", "default": false },
        "hideReplyInThreadOption": { "type": "boolean", "default": false },
        "hidePinMessageOption": { "type": "boolean", "default": false },
        "hideUnpinMessageOption": { "type": "boolean", "default": false },
        "hideSaveMessageOption": { "type": "boolean", "default": false },
        "hideUnsaveMessageOption": { "type": "boolean", "default": false },
        "hideThreadSubscriptionOption": { "type": "boolean", "default": false },
        "hideEditMessageOption": { "type": "boolean", "default": false },
        "hideDeleteMessageOption": { "type": "boolean", "default": false },
        "hideCopyMessageOption": { "type": "boolean", "default": false },
        "hideReactionOption": { "type": "boolean", "default": false },
        "hideMessageInfoOption": { "type": "boolean", "default": false },
        "hideFlagMessageOption": { "type": "boolean", "default": false },
        "hideMessagePrivatelyOption": { "type": "boolean", "default": false },
        "hideTranslateMessageOption": { "type": "boolean", "default": false },
        "hideFlagRemarkField": { "type": "boolean", "default": false },
        "quickOptionsCount": { "type": "number", "default": 3 }
      },
      "dateFormatting": {
        "separatorDateTimeFormat": { "type": "CometChatDateFormatConfig" },
        "stickyDateTimeFormat": { "type": "CometChatDateFormatConfig" },
        "messageSentAtDateTimeFormat": { "type": "CometChatDateFormatConfig" },
        "messageInfoDateTimeFormat": { "type": "CometChatDateFormatConfig" }
      },
      "alignment": {
        "messageAlignment": {
          "type": "CometChatMessageListAlignment",
          "values": ["0 (left)", "1 (standard)"],
          "default": "1 (standard)"
        }
      },
      "sound": {
        "disableSoundForMessages": { "type": "boolean", "default": false },
        "customSoundForMessages": { "type": "string", "default": "built-in" }
      },
      "ai": {
        "smartRepliesKeywords": {
          "type": "string[]",
          "default": "['what','when','why','who','where','how','?']"
        },
        "smartRepliesDelayDuration": {
          "type": "number",
          "default": 10000
        }
      },
      "callbacks": {
        "onThreadRepliesClick": "(message: CometChat.BaseMessage) => void",
        "onAvatarClick": "(user: CometChat.User) => void",
        "onEditMessage": "(message: CometChat.BaseMessage) => void",
        "onReplyMessage": "(message: CometChat.BaseMessage) => void",
        "onReactionClick": "(reaction: CometChat.ReactionCount, message: CometChat.BaseMessage) => void",
        "onReactionListItemClick": "(reaction: CometChat.Reaction, message: CometChat.BaseMessage) => void",
        "onActiveChatChanged": "(data: { user?, group?, message?, unreadMessageCount? }) => void",
        "onMessageRead": "(message: CometChat.BaseMessage) => void",
        "onMessageDeleted": "(message: CometChat.BaseMessage) => void",
        "onConversationMarkedAsRead": "(conversation: CometChat.Conversation) => void",
        "onConversationUpdated": "(conversation: CometChat.Conversation) => void",
        "onError": "((error: CometChat.CometChatException) => void) | null"
      },
      "formatting": {
        "textFormatters": { "type": "CometChatTextFormatter[]", "default": "undefined", "since": "7.2.0", "note": "Merged into text and caption bubbles." }
      },
      "viewSlots": {
        "bubbleView": "(message: CometChat.BaseMessage, loggedInUser: CometChat.User) => ReactNode",
        "headerView": "ReactNode",
        "footerView": "ReactNode",
        "loadingView": "ReactNode",
        "emptyView": "ReactNode",
        "errorView": "ReactNode"
      }
    },
    "events": [
      {
        "name": "ui:message/read",
        "payload": "{ message }",
        "description": "Message marked as read"
      },
      {
        "name": "ui:message/deleted",
        "payload": "{ message }",
        "description": "Message deleted by user"
      },
      {
        "name": "ui:active-chat/changed",
        "payload": "{ user?, group?, message?, unreadMessageCount? }",
        "description": "Active chat context changed on init"
      },
      {
        "name": "ui:conversation/read",
        "payload": "{ conversationId }",
        "description": "Conversation marked as read"
      },
      {
        "name": "ui:conversation/updated",
        "payload": "{ conversation }",
        "description": "Conversation updated (mark-as-unread)"
      }
    ],
    "sdkListeners": [
      "onTextMessageReceived",
      "onMediaMessageReceived",
      "onCustomMessageReceived",
      "onInteractiveMessageReceived",
      "onTypingStarted",
      "onTypingEnded",
      "onMessagesDelivered",
      "onMessagesRead",
      "onMessagesDeliveredToAll",
      "onMessagesReadByAll",
      "onMessageEdited",
      "onMessageDeleted",
      "onTransientMessageReceived"
    ],
    "types": {
      "CometChatMessageListAlignment": {
        "left": 0,
        "standard": 1
      },
      "CometChatDateFormatConfig": {
        "today": "string | undefined",
        "yesterday": "string | undefined",
        "lastWeek": "string | undefined",
        "otherDays": "string | undefined",
        "relativeTime": {
          "minute": "string | undefined",
          "minutes": "string | undefined",
          "hour": "string | undefined",
          "hours": "string | undefined"
        }
      }
    }
  }
  ```
</Accordion>

## Overview

`CometChatMessageList` is a scrollable message feed component. It renders messages for a given user or group conversation and emits thread navigation via `onThreadRepliesClick`. Wire it between `CometChatMessageHeader` and `CometChatMessageComposer` to build a standard chat panel.

<Info>
  **Live Preview** — interact with the default message list.

  [Open in Storybook ↗](https://storybook.cometchat.io/react/?path=/story/components-messages-cometchat-message-list--default)
</Info>

<iframe src="https://storybook.cometchat.io/react/iframe.html?id=components-messages-cometchat-message-list--default&viewMode=story&shortcuts=false&singleStory=true" className="w-full rounded-xl" loading="lazy" style={{height: "700px", border: "1px solid #e0e0e0"}} title="CometChat Message List — Default" allow="clipboard-write" />

The component handles:

* Paginated message fetching with infinite scroll (older messages load on scroll-up)
* Plugin-based bubble rendering — each message type is rendered by a registered plugin
* Reactions with emoji picker and reaction list
* Read/delivery receipts (single and group)
* Thread reply indicators with reply count
* Real-time updates (new messages, edits, deletes, typing indicators)
* Smart replies — AI-generated quick reply suggestions based on incoming messages
* Conversation starters — AI-generated prompts when the conversation is empty
* Date separators and sticky floating date header
* Jump-to-message for search results and deep links
* Mark-as-unread support

> **Note:** `user` and `group` are mutually exclusive. Pass one or the other to specify the conversation context.

***

## Usage

### Flat API

```tsx theme={null}
import { CometChatMessageList } from "@cometchat/chat-uikit-react";

function ChatPanel({ user }: { user: CometChat.User }) {
  return (
    <CometChatMessageList
      user={user}
      onThreadRepliesClick={(message) => openThread(message)}
    />
  );
}
```

### Compound Composition

```tsx theme={null}
import { CometChatMessageList } from "@cometchat/chat-uikit-react";

function ChatPanel({ group }: { group: CometChat.Group }) {
  return (
    <CometChatMessageList.Root group={group}>
      <CometChatMessageList.LoadingState />
      <CometChatMessageList.ErrorState />
      <CometChatMessageList.EmptyState />
      <CometChatMessageList.Header />
      <CometChatMessageList.View />
      <CometChatMessageList.Footer />
      <CometChatMessageList.AIFooter />
    </CometChatMessageList.Root>
  );
}
```

### Full Layout Example

```tsx theme={null}
import { useState } from "react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import {
  CometChatConversations,
  CometChatMessageHeader,
  CometChatMessageList,
  CometChatMessageComposer,
} from "@cometchat/chat-uikit-react";

function ChatApp() {
  const [user, setUser] = useState<CometChat.User | undefined>();
  const [group, setGroup] = useState<CometChat.Group | undefined>();
  const [threadMessage, setThreadMessage] = useState<CometChat.BaseMessage | undefined>();

  const handleConversationClick = (conversation: CometChat.Conversation) => {
    const entity = conversation.getConversationWith();
    if (conversation.getConversationType() === "user") {
      setUser(entity as CometChat.User);
      setGroup(undefined);
    } else {
      setGroup(entity as CometChat.Group);
      setUser(undefined);
    }
    setThreadMessage(undefined);
  };

  return (
    <div style={{ display: "flex", height: "100vh" }}>
      <div style={{ width: 360 }}>
        <CometChatConversations onItemClick={handleConversationClick} />
      </div>
      <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
        <CometChatMessageHeader user={user} group={group} />
        <CometChatMessageList
          user={user}
          group={group}
          onThreadRepliesClick={(message) => setThreadMessage(message)}
        />
        <CometChatMessageComposer user={user} group={group} />
      </div>
      {threadMessage && (
        <div style={{ width: 400, display: "flex", flexDirection: "column" }}>
          <CometChatMessageList
            user={user}
            group={group}
            parentMessage={threadMessage}
          />
          <CometChatMessageComposer
            user={user}
            group={group}
            parentMessageId={threadMessage.getId()}
          />
        </div>
      )}
    </div>
  );
}
```

***

## Multi-Attachment Batch Grouping

When several attachments are sent together from the [composer's staging tray](/ui-kit/react/components/message-composer#multi-attachment-staging-tray), they arrive as multiple media messages — one per media kind, in the fixed order **images → videos → audio → files** — that all share a single `batchId`. The message list detects consecutive messages carrying the same `batchId` **from the same sender** and renders them as one visually connected group instead of separate, independently-chromed bubbles.

<Info>
  **Live Preview** — interact with the mixed multi attachment message list.

  [Open in Storybook ↗](https://storybook.cometchat.io/react/?path=/story/components-bubbles-message-bubble-batch-multi-attachment--incoming-batch-group)
</Info>

<iframe src="https://storybook.cometchat.io/react/iframe.html?id=components-bubbles-message-bubble-batch-multi-attachment--incoming-batch-group&viewMode=story&shortcuts=false&singleStory=true" className="w-full rounded-xl" loading="lazy" style={{height: "860px", border: "1px solid #e0e0e0"}} title="CometChat Message List — Mixed Multi attachment batch" allow="clipboard-write" />

### Batch position

Each message in a batch is assigned a `batchPosition` that the list applies to its message-bubble wrapper to adjust corner radius so the group reads as a single stack:

| Position | Meaning | Corners |
| - | - | - |
| `first` | Top message of a multi-message batch | Rounded top, flat bottom |
| `middle` | Interior message | Flat top and bottom |
| `last` | Bottom message of a multi-message batch | Flat top, rounded bottom |
| `single` | Standalone message (not part of a batch) | All corners rounded |

### Grouped chrome

Within a batch, the list renders shared chrome so the group looks like one logical message rather than several:

* The **avatar and sender name** render once, on the `first` message.
* **Delivery/read receipts** render once, on the `last` message.
* Interior spacing collapses so the bubbles sit tightly and read as a single stack.
* The **caption** (the composer's text), if any, is carried by the `last` message.

This is computed automatically — you don't configure anything. It applies whenever messages share a `batchId`, which the composer sets on every multi-attachment send.

***

## Filtering

Pass a `CometChat.MessagesRequestBuilder` to `messagesRequestBuilder` to control which messages load. Pass the builder instance — not the result of `.build()`. Refer to [MessagesRequestBuilder](/sdk/javascript/message-filtering) for the full builder API.

```tsx theme={null}
<CometChatMessageList
  user={user}
  messagesRequestBuilder={
    new CometChat.MessagesRequestBuilder()
      .setLimit(50)
      .setCategories(["message"])
      .setTypes(["text", "image", "video"])
  }
/>
```

### Filter Recipes

| Recipe | Code |
| - | - |
| Only text messages | `new CometChat.MessagesRequestBuilder().setTypes(["text"])` |
| Only media | `new CometChat.MessagesRequestBuilder().setTypes(["image", "video", "audio", "file"])` |
| Larger page size | `new CometChat.MessagesRequestBuilder().setLimit(50)` |
| Hide actions | `new CometChat.MessagesRequestBuilder().setCategories(["message"])` |
| With tags | `new CometChat.MessagesRequestBuilder().setTags(["important"])` |

***

## Actions and Events

### Callback Props

| Prop | Signature | Fires when |
| - | - | - |
| `onThreadRepliesClick` | `(message: CometChat.BaseMessage) => void` | User clicks the thread reply indicator — wire this to open a thread panel ([Threaded Messages guide](/ui-kit/react/guide-threaded-messages)) |
| `onAvatarClick` | `(user: CometChat.User) => void` | User clicks an avatar on an incoming message |
| `onEditMessage` | `(message: CometChat.BaseMessage) => void` | User selects "Edit" from context menu |
| `onReplyMessage` | `(message: CometChat.BaseMessage) => void` | User selects "Reply" from context menu |
| `onReactionClick` | `(reaction: CometChat.ReactionCount, message: CometChat.BaseMessage) => void` | User clicks a reaction count pill |
| `onReactionListItemClick` | `(reaction: CometChat.Reaction, message: CometChat.BaseMessage) => void` | User clicks an individual reactor in the reaction list |
| `onActiveChatChanged` | `(data: { user?, group?, message?, unreadMessageCount? }) => void` | Active chat context set on initial load |
| `onMessageRead` | `(message: CometChat.BaseMessage) => void` | A message is marked as read |
| `onMessageDeleted` | `(message: CometChat.BaseMessage) => void` | A message is deleted |
| `onConversationMarkedAsRead` | `(conversation: CometChat.Conversation) => void` | Conversation marked as read |
| `onConversationUpdated` | `(conversation: CometChat.Conversation) => void` | Conversation updated (e.g., mark-as-unread) |
| `onError` | `((error: CometChat.CometChatException) => void) \| null` | SDK error occurs |

### Events Emitted

UI events this component publishes:

| Event | Payload | Fires when |
| - | - | - |
| `ui:message/read` | `{ message }` | Message marked as read (auto on scroll-to-bottom) |
| `ui:message/deleted` | `{ message }` | Message deleted by user via context menu |
| `ui:active-chat/changed` | `{ user?, group?, message?, unreadMessageCount? }` | Active chat context set on initial load |
| `ui:conversation/read` | `{ conversationId }` | Conversation marked as read (on init or scroll-to-bottom) |
| `ui:conversation/updated` | `{ conversation }` | Conversation updated (e.g., mark-as-unread) |
| `ui:thread/subscription-changed` | `{ parentMessageId, subscribed }` | The user toggles the subscribe/unsubscribe option, or an auto-subscription fires (the user sends a reply, or is @mentioned in one) — mirrored to the parent whether or not the thread panel is open |

<Note>
  The "Edit" and "Reply" context menu options publish `ui:compose/edit` and `ui:compose/reply` events respectively. These are emitted by the message options system (plugins), not by the MessageList component directly. The MessageComposer subscribes to these events to enter edit/reply mode.
</Note>

### Events Received

UI events this component subscribes to (published by other components) — part of the UI Kit's [Event System](/ui-kit/react/event-system). These let group/call/composer actions elsewhere in your app update the list automatically:

| Event | Payload | Behavior |
| - | - | - |
| `ui:message/sent` | `{ message, status }` | Appends sent message (optimistic flow from composer) |
| `ui:compose/edit` | `{ message, status }` | Updates edited message in the list |
| `ui:group/member-added` | `{ group, members, messages }` | Appends group action message |
| `ui:group/member-kicked` | `{ group, user, message }` | Appends group action message |
| `ui:group/member-banned` | `{ group, user, message }` | Appends group action message |
| `ui:group/member-unbanned` | `{ group, user, message }` | Appends group action message |
| `ui:group/member-scope-changed` | `{ group, user, newScope }` | Appends group action message |
| `ui:group/left` | `{ group }` | Appends group action message |
| `ui:group/ownership-changed` | `{ group, newOwner, previousOwnerUid }` | Appends group action message |
| `ui:call/outgoing` | `{ call }` | Appends call message |
| `ui:call/accepted` | `{ call }` | Appends call message |
| `ui:call/rejected` | `{ call }` | Appends call message |
| `ui:call/ended` | `{ call? }` | Appends call message |

### SDK Listeners (Automatic)

These SDK listeners are attached internally. The component updates its state automatically — no manual subscription needed:

* Message listeners: `onTextMessageReceived`, `onMediaMessageReceived`, `onCustomMessageReceived`, `onInteractiveMessageReceived`
* Typing: `onTypingStarted`, `onTypingEnded`
* Receipts: `onMessagesDelivered`, `onMessagesRead`, `onMessagesDeliveredToAll`, `onMessagesReadByAll`
* Edits & deletes: `onMessageEdited`, `onMessageDeleted`
* Transient: `onTransientMessageReceived`

***

## Customization

### Pin and Save Options

When pinning and saving are enabled for your app (the `features.ux.messages.pinned.enabled` and `features.ux.messages.saved.enabled` app settings), `CometChatMessageList` automatically adds **Pin**, **Unpin**, **Save**, and **Unsave** to the message context menu — no props required. The **Pin/Unpin option is shown to every member** — the client does not gate it by role. The **server** enforces who may pin or unpin in that conversation; a denied attempt is rejected and surfaces a permission toast (localized via the `action_permission_denied` key). Saving is per-user and always available.

Use the `hide*` props to remove any of these options:

```tsx theme={null}
<CometChatMessageList
  group={group}
  hideSaveMessageOption   // drop Save/Unsave from the menu
  hideUnsaveMessageOption
/>
```

| Prop | Removes |
| - | - |
| `hidePinMessageOption` | Pin |
| `hideUnpinMessageOption` | Unpin |
| `hideSaveMessageOption` | Save |
| `hideUnsaveMessageOption` | Unsave |
| `hideThreadSubscriptionOption` | Subscribe / unsubscribe thread |

See the [Pin & Save Messages guide](/ui-kit/react/guide-pin-and-save-messages) for the full screen, and [Core Features → Pin & Save](/ui-kit/react/core-features#pin-and-save-messages) for enabling the features.

### View Props

Use view props to replace sections of the default UI while keeping the component's behavior intact:

```tsx theme={null}
<CometChatMessageList
  user={user}
  headerView={<TypingIndicator />}
  footerView={<SmartRepliesBar />}
  emptyView={<EmptyConversation />}
  loadingView={<MessageSkeleton />}
  errorView={<ErrorBanner />}
  bubbleView={(message, loggedInUser) => <CustomBubble message={message} />}
/>
```

| Slot | Signature | Replaces |
| - | - | - |
| `bubbleView` | `(message, loggedInUser) => ReactNode` | Entire bubble rendering for each message |
| `headerView` | `ReactNode` | Header area above the scroll container |
| `footerView` | `ReactNode` | Footer area below the scroll container |
| `loadingView` | `ReactNode` | Loading shimmer |
| `emptyView` | `ReactNode` | Empty state |
| `errorView` | `ReactNode` | Error state |

#### headerView

Custom view above the message list.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-docs-llms-scoped-indexes/2jSVlYXfUL_4P-jB/images/3096c772-message_list_headerview_web_screens-c088625ffa7a992eeac85df186c320ec.png?fit=max&auto=format&n=2jSVlYXfUL_4P-jB&q=85&s=0aa221b7c5682fd2db98b6725d1202aa" width="1282" height="802" data-path="images/3096c772-message_list_headerview_web_screens-c088625ffa7a992eeac85df186c320ec.png" />
</Frame>

<Tabs>
  <Tab title="TypeScript">
    ```tsx theme={null}
    import { useState, useEffect } from "react";
    import { CometChat } from "@cometchat/chat-sdk-javascript";
    import { CometChatMessageList, CometChatButton } from "@cometchat/chat-uikit-react";

    function MessageListWithHeader() {
      const [chatUser, setChatUser] = useState<CometChat.User>();

      useEffect(() => {
        CometChat.getUser("uid").then((user) => setChatUser(user));
      }, []);

      return chatUser ? (
        <CometChatMessageList
          user={chatUser}
          headerView={
            <div className="header-view">
              <CometChatButton.Root>
                <CometChatButton.Text>Notes</CometChatButton.Text>
              </CometChatButton.Root>
              <CometChatButton.Root>
                <CometChatButton.Text>Pinned Message</CometChatButton.Text>
              </CometChatButton.Root>
              <CometChatButton.Root>
                <CometChatButton.Text>Saved Links</CometChatButton.Text>
              </CometChatButton.Root>
            </div>
          }
        />
      ) : null;
    }
    ```
  </Tab>

  <Tab title="CSS">
    ```css theme={null}
    .header-view {
      display: flex;
      width: 100%;
      padding: 3px 16px;
      align-items: flex-start;
      gap: 5px;
      background: #edeafa;
    }

    .header-view .cometchat-button {
      width: auto;
      height: 32px;
      border-radius: 1000px;
      border: 1px solid #e8e8e8;
      background: #fafafa;
    }

    .header-view .cometchat-button__text {
      color: #6852d6;
      font: 400 12px/14.4px Roboto;
    }
    ```
  </Tab>
</Tabs>

#### footerView

Custom view below the message list.

<Frame>
  <img src="https://mintcdn.com/cometchat-22654f5b-docs-llms-scoped-indexes/LUAnk4kNde8CLq7p/images/82f25a7d-message_list_footerview_web_screens-c277b86d74f0cdeb81c6ce7a7251383a.png?fit=max&auto=format&n=LUAnk4kNde8CLq7p&q=85&s=bb8a2acd77c4e18fa62b73644b4bb1a7" width="1282" height="802" data-path="images/82f25a7d-message_list_footerview_web_screens-c277b86d74f0cdeb81c6ce7a7251383a.png" />
</Frame>

<Tabs>
  <Tab title="TypeScript">
    ```tsx theme={null}
    import { CometChat } from "@cometchat/chat-sdk-javascript";
    import { CometChatMessageList, CometChatButton } from "@cometchat/chat-uikit-react";

    function MessageListWithFooter({ chatUser }: { chatUser: CometChat.User }) {
      return (
        <CometChatMessageList
          user={chatUser}
          footerView={
            <div className="footer-view">
              <CometChatButton.Root>
                <CometChatButton.Text>Ice Breakers</CometChatButton.Text>
              </CometChatButton.Root>
              <CometChatButton.Root>
                <CometChatButton.Text>+1-212-456-7890</CometChatButton.Text>
              </CometChatButton.Root>
            </div>
          }
        />
      );
    }
    ```
  </Tab>

  <Tab title="CSS">
    ```css theme={null}
    .footer-view {
      display: flex;
      width: 100%;
      padding: 3px 16px;
      align-items: flex-start;
      gap: 5px;
      background: #edeafa;
    }
    ```
  </Tab>
</Tabs>

### Compound Composition

For full layout control, use sub-components. Omit any sub-component to hide it:

```tsx theme={null}
<CometChatMessageList.Root user={user}>
  {/* No Header — it won't render */}
  <CometChatMessageList.LoadingState />
  <CometChatMessageList.ErrorState />
  <CometChatMessageList.EmptyState />
  <CometChatMessageList.View />
  <CometChatMessageList.AIFooter />
</CometChatMessageList.Root>
```

Available sub-components:

| Sub-component | Description | Props | Flat API equivalent |
| - | - | - | - |
| `Root` | Context provider, data hook, container | All Root props + `children` | — |
| `View` | Scrollable message feed | `className` | — |
| `Header` | Content above the feed | `children`, `className` | `headerView` |
| `Footer` | Content below the feed | `children`, `className` | `footerView` |
| `ScrollToBottom` | Floating scroll button | `className` | — |
| `EmptyState` | Empty state | `children`, `className` | `emptyView` |
| `ErrorState` | Error state | `children`, `className` | `errorView` |
| `LoadingState` | Loading shimmer | `children`, `className` | `loadingView` |
| `DateSeparator` | Day separator | `className` | — |
| `AIFooter` | Smart replies / starters | `className` | — |

### CSS Styling

Override design tokens on the component selector:

```css theme={null}
.cometchat-message-list {
  --cometchat-background-color-01: #1a1a2e;
  --cometchat-text-color-primary: #ffffff;
}

.cometchat-message-list__date-separator {
  --cometchat-text-color-secondary: #aaa;
}
```

***

## Props

All props are optional (though you must pass either `user` or `group` to render messages).

<Note>
  View slot props (`bubbleView`, `headerView`, `footerView`, `loadingView`, `emptyView`, `errorView`) are convenience props available only on the flat API. In compound composition mode, use the corresponding sub-components directly.
</Note>

***

### Entity

<Note>
  The message list requires exactly one entity — a `user` (1:1) or a `group` — to know which conversation to render. Without either prop it shows nothing. For how to derive the active entity from a conversation/user/group selection and mount the list, see the [New Chat Creation guide](/ui-kit/react/guide-new-chat-creation).
</Note>

***

### user

The user entity for a 1-on-1 conversation. Mutually exclusive with `group`.

| | |
| - | - |
| Type | `CometChat.User` |
| Default | `undefined` |

***

### group

The group entity for a group conversation. Mutually exclusive with `user`.

| | |
| - | - |
| Type | `CometChat.Group` |
| Default | `undefined` |

***

### parentMessage

Enables thread mode. When set, the component fetches and displays replies to this parent message instead of the main conversation feed. Prefer this over [`parentMessageId`](#parentmessageid): holding the whole parent message lets the list read the thread's live subscription state and stamp it onto realtime-arriving replies (a socket-delivered reply carries no flag of its own). Re-pass it when the parent updates in real time.

| | |
| - | - |
| Type | `CometChat.BaseMessage` |
| Default | `undefined` |

***

### parentMessageId

**Deprecated — pass [`parentMessage`](#parentmessage) instead.** Enables thread mode by id. Still honoured (as the parent's id) when `parentMessage` is absent, so existing integrations keep working; new code should pass the whole `parentMessage`.

| | |
| - | - |
| Type | `number` |
| Default | `undefined` |

<Note>
  Thread replies require wiring: capture the parent from `onThreadRepliesClick`, then mount a **second** `CometChatMessageList` + `CometChatMessageComposer` with this `parentMessageId` (usually alongside a `CometChatThreadHeader`) in a thread panel. See the [Threaded Messages guide](/ui-kit/react/guide-threaded-messages) for the full pattern.
</Note>

***

### Data

***

### messagesRequestBuilder

Custom request builder for fetching messages. Controls pagination, filtering, and sorting.

| | |
| - | - |
| Type | `CometChat.MessagesRequestBuilder` |
| Default | SDK default (limit 30) |

***

### reactionsRequestBuilder

Custom request builder for fetching reactions on messages.

| | |
| - | - |
| Type | `CometChat.ReactionsRequestBuilder` |
| Default | `undefined` |

***

### messageTypes

Restrict which message types are fetched and displayed.

| | |
| - | - |
| Type | `string[]` |
| Default | Plugin registry types |

***

### messageCategories

Restrict which message categories are fetched and displayed.

| | |
| - | - |
| Type | `string[]` |
| Default | Plugin registry categories |

***

### goToMessageId

Jump to a specific message by ID (e.g., from search results or a deep link). The list scrolls to and highlights the target message.

| | |
| - | - |
| Type | `number` |
| Default | `undefined` |

<Note>
  This is how in-conversation search "jump to result" and deep links work: take the message ID from `CometChatSearch` (or your deep link) and pass it here. See the [Search Messages guide](/ui-kit/react/guide-search-messages) for the end-to-end wiring.
</Note>

***

### startFromUnreadMessages

When `true`, the list scrolls to the first unread message on open instead of the bottom.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### loadLastAgentConversation

When `true`, loads the last agent conversation on initial render. Used for AI agent chat flows.

<Note>
  Agent chat props (`loadLastAgentConversation`, `isAgentChat`) require an **AI Agent configured in the CometChat Dashboard** — the conversation entity must be that agent user. For a purpose-built agent experience, use the [AI Assistant Chat](/ui-kit/react/components/ai-assistant-chat) component, which wraps this list. See [AI Features](/ui-kit/react/ai-features) for the broader AI capabilities.
</Note>

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### Visibility

***

### hideReceipts

Hide message delivery/read receipts (double ticks) on message bubbles.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideStickyDate

Hide the sticky date header that floats at the top of the scroll area.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideDateSeparator

Hide date separators between calendar days in the message feed.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideAvatar

Hide the avatar on incoming messages in group conversations.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideGroupActionMessages

Filter out group action messages (member joined, left, kicked, etc.).

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideModerationView

Hide the moderation footer beneath disapproved messages.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

<Note>
  The moderation footer only appears when messages are actually moderated, which requires **Moderation rules configured in the CometChat Dashboard**. See [Moderation overview](/moderation/overview) to set up rules. This prop only controls whether the UI Kit renders the footer for already-moderated messages.
</Note>

***

### showScrollbar

Show the native scrollbar on the message list. When `false`, the scrollbar is hidden but scrolling still works.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### showSmartReplies

Show AI-generated smart reply suggestions in the footer when the last received message matches keyword criteria.

<Note>
  This requires the **Smart Replies** AI feature to be enabled for your app in the CometChat Dashboard. With the feature disabled, no suggestions are generated even when `showSmartReplies` is `true`. See [AI Features](/ui-kit/react/ai-features#smart-replies).
</Note>

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### showConversationStarters

Show AI-generated conversation starters in the footer when the message list is empty.

<Note>
  This requires the **Conversation Starter** AI feature to be enabled for your app in the CometChat Dashboard. With the feature disabled, no starters are generated even when `showConversationStarters` is `true`. See [AI Features](/ui-kit/react/ai-features#conversation-starter).
</Note>

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### showMarkAsUnreadOption

Show the "Mark as Unread" option in the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### disableTruncation

Disable text truncation (read more / show less) in text bubbles. When `true`, full message text is always shown.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### isAgentChat

Whether this is an AI agent chat. Suppresses moderation UI and enables agent-specific behaviors.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### Option Toggles

***

### hideReplyOption

Hide the "Reply" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideReplyInThreadOption

Hide the "Reply in Thread" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hidePinMessageOption

Hide the "Pin" option from the message context menu. Pinning must also be enabled for your app (the `features.ux.messages.pinned.enabled` app setting).

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideUnpinMessageOption

Hide the "Unpin" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideSaveMessageOption

Hide the "Save" option from the message context menu. Saving must also be enabled for your app (the `features.ux.messages.saved.enabled` app setting).

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideUnsaveMessageOption

Hide the "Unsave" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideThreadSubscriptionOption

Hide the thread subscription (subscribe/unsubscribe) option from the message context menu. See [Threaded Messages → Thread Subscription](/ui-kit/react/guide-threaded-messages#thread-subscription).

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideEditMessageOption

Hide the "Edit" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideDeleteMessageOption

Hide the "Delete" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideCopyMessageOption

Hide the "Copy" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideReactionOption

Hide the "React" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideMessageInfoOption

Hide the "Message Info" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideFlagMessageOption

Hide the "Flag/Report" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

<Note>
  Flagging/reporting a message feeds CometChat **Moderation**. Flagged messages are reviewed in the [Moderation dashboard](/moderation/overview), and custom report reasons are configured there. See [Moderation overview](/moderation/overview).
</Note>

***

### hideMessagePrivatelyOption

Hide the "Message Privately" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### hideTranslateMessageOption

Hide the "Translate" option from the message context menu.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

<Note>
  The "Translate" option only works when the **Message Translation extension is enabled** in the CometChat Dashboard. Without it, the option has nothing to call. Enable it from [Extensions](/ui-kit/react/extensions) — see the [Message Translation guide](/fundamentals/message-translation).
</Note>

***

### hideFlagRemarkField

Hide the remark textarea in the flag/report message dialog.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### quickOptionsCount

Number of quick-action options shown inline next to the bubble before overflowing into the "More" menu.

| | |
| - | - |
| Type | `number` |
| Default | `3` |

***

### Date Formatting

***

### separatorDateTimeFormat

Custom date format for in-list day separators ("Today", "Yesterday", etc.).

| | |
| - | - |
| Type | `CometChatDateFormatConfig` |
| Default | Built-in format |

***

### stickyDateTimeFormat

Custom date format for the sticky date header floating above messages.

| | |
| - | - |
| Type | `CometChatDateFormatConfig` |
| Default | Built-in format |

***

### messageSentAtDateTimeFormat

Custom date format for the timestamp shown beside the message bubble.

| | |
| - | - |
| Type | `CometChatDateFormatConfig` |
| Default | Built-in format |

***

### messageInfoDateTimeFormat

Custom date format for timestamps in the MessageInformation sheet.

| | |
| - | - |
| Type | `CometChatDateFormatConfig` |
| Default | Built-in format |

***

### Alignment

***

### messageAlignment

Controls message bubble alignment. `0` aligns all messages to the left. `1` uses standard alignment (sent on right, received on left).

| | |
| - | - |
| Type | `CometChatMessageListAlignment` |
| Default | `1` (standard) |

***

### Sound

***

### disableSoundForMessages

Disable the notification sound when new messages arrive.

| | |
| - | - |
| Type | `boolean` |
| Default | `false` |

***

### customSoundForMessages

Custom sound URL to play when new messages arrive (replaces the built-in sound).

| | |
| - | - |
| Type | `string` |
| Default | Built-in notification sound |

***

### AI Features

***

### smartRepliesKeywords

Keywords that trigger smart replies when found in a received text message. If set to an empty array, smart replies show for every received text message.

| | |
| - | - |
| Type | `string[]` |
| Default | `['what', 'when', 'why', 'who', 'where', 'how', '?']` |

***

### smartRepliesDelayDuration

Delay in milliseconds before showing smart replies after a qualifying message is received. Acts as a debounce — if another message arrives within this window, the timer resets.

| | |
| - | - |
| Type | `number` |
| Default | `10000` |

***

### Callbacks

***

### onThreadRepliesClick

Callback when the thread reply indicator is clicked on a message bubble.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onAvatarClick

Callback when an incoming message avatar is clicked.

| | |
| - | - |
| Type | `(user: CometChat.User) => void` |
| Default | `undefined` |

***

### onEditMessage

Callback when the "Edit" context menu option is clicked. Typically sets the message in edit mode in the composer.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onReplyMessage

Callback when the "Reply" context menu option is clicked. Typically sets the message as reply-to in the composer.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onReactionClick

Callback when a reaction count pill is clicked on a message.

| | |
| - | - |
| Type | `(reaction: CometChat.ReactionCount, message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onReactionListItemClick

Callback when an individual reactor in the reaction list is clicked.

| | |
| - | - |
| Type | `(reaction: CometChat.Reaction, message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onActiveChatChanged

Callback invoked on first load with the active chat context (user/group, last message, unread count).

| | |
| - | - |
| Type | `(data: { user?, group?, message?, unreadMessageCount? }) => void` |
| Default | `undefined` |

***

### onMessageRead

Callback when a message is marked as read.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onMessageDeleted

Callback when a message is deleted.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage) => void` |
| Default | `undefined` |

***

### onConversationMarkedAsRead

Callback when the conversation is marked as read.

| | |
| - | - |
| Type | `(conversation: CometChat.Conversation) => void` |
| Default | `undefined` |

***

### onConversationUpdated

Callback when mark-as-unread succeeds (conversation updated).

| | |
| - | - |
| Type | `(conversation: CometChat.Conversation) => void` |
| Default | `undefined` |

***

### onError

Callback when an SDK error occurs during fetch or real-time operations.

| | |
| - | - |
| Type | `((error: CometChat.CometChatException) => void) \| null` |
| Default | `null` |

***

### View Slots

***

### bubbleView

Replace the entire bubble rendering for each message. When provided, the default plugin-based BubbleRenderer is skipped entirely.

| | |
| - | - |
| Type | `(message: CometChat.BaseMessage, loggedInUser: CometChat.User) => ReactNode` |
| Default | `undefined` |

```tsx theme={null}
<CometChatMessageList
  user={user}
  bubbleView={(message, loggedInUser) => (
    <div className="my-bubble">
      {message.getText?.() || "Custom bubble"}
    </div>
  )}
/>
```

***

### headerView

Custom content rendered above the scroll container (e.g., typing indicator, pinned message).

| | |
| - | - |
| Type | `ReactNode` |
| Default | `undefined` |

***

### footerView

Custom content rendered below the scroll container (e.g., smart replies bar).

| | |
| - | - |
| Type | `ReactNode` |
| Default | `undefined` |

***

### loadingView

Custom loading state shown while messages are being fetched.

| | |
| - | - |
| Type | `ReactNode` |
| Default | Built-in shimmer |

***

### emptyView

Custom empty state shown when there are no messages in the conversation.

| | |
| - | - |
| Type | `ReactNode` |
| Default | Built-in empty state |

***

### errorView

Custom error state shown when message fetching fails.

| | |
| - | - |
| Type | `ReactNode` |
| Default | Built-in error state |

***

### textFormatters

Custom display formatters merged into the text and caption bubbles rendered by the list.

| | |
| - | - |
| Type | `CometChatTextFormatter[]` |
| Default | `undefined` |
| Since | 7.2.0 |

```tsx theme={null}
<CometChatMessageList group={group} textFormatters={[new ColorFormatter()]} />
```

<Note>
  Available from **7.2.0**. The prop existed in v6, was dropped in 7.0.0 in favour of the plugin system, and returned in 7.2.0. A v6 formatter carries over when its transform lives in `customLogicToFormatText()` or `getFormattedText()`; in TypeScript, add `readonly id = "…"`, since `id` is now abstract. A transform in `onRegexMatch()` never runs in v7, and members v7 removed (`setStyle()`, `setClasses()` and the rest) throw when called — inside the display pipeline that error is caught and your formatter is skipped rather than surfaced. See [Porting a v6 formatter](/ui-kit/react/migration-property-changes#porting-a-v6-formatter) and [Text Formatters](/ui-kit/react/plugins/text-formatters). On 7.0.x–7.1.x the prop is not available.

  Register the same formatters on `CometChatMessageComposer` as well. If you format only in the composer, the author sees the styled result while readers see the raw marker text — see the [Custom Text Formatter guide](/ui-kit/react/guide-custom-text-formatter).
</Note>

***

## CSS Selectors

| Target | Selector |
| - | - |
| Root container | `.cometchat-message-list` |
| Header area | `.cometchat-message-list__header` |
| Footer area | `.cometchat-message-list__footer` |
| Scroll container | `.cometchat-message-list__scroll-container` |
| Date separator | `.cometchat-message-list__date-separator` |
| Floating sticky date | `.cometchat-message-list__floating-date` |
| New message divider | `.cometchat-message-list__new-message-divider` |
| Scroll-to-bottom button | `.cometchat-message-list__scroll-to-bottom` |
| Empty state | `.cometchat-message-list__empty-state` |
| Error state | `.cometchat-message-list__error-state` |
| Loading state | `.cometchat-message-list__loading-state` |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Message Composer" icon="pen" href="/ui-kit/react/components/message-composer">
    Add a composer below the message list for sending messages
  </Card>

  <Card title="Thread Header" icon="comments" href="/ui-kit/react/components/thread-header">
    Display thread context above a threaded message list
  </Card>

  <Card title="Plugins Overview" icon="puzzle-piece" href="/ui-kit/react/plugins/overview">
    Learn how plugins register custom bubble renderers
  </Card>

  <Card title="Theming" icon="paintbrush" href="/ui-kit/react/theming">
    Customize colors, fonts, and spacing
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.