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

# Event System

> Unified pub/sub event system that merges SDK listener events with local UI events for cross-component communication.

<Accordion title="AI Integration Quick Reference">
  | Field | Value |
  | - | - |
  | Subscribe hook | `useCometChatEvents((event) => { ... }, [deps])` — `deps` is required |
  | Publish hook | `const publish = usePublishEvent()` |
  | Provider | `CometChatEventsProvider` (mounted automatically by `CometChatProvider` after login) |
  | Event prefix | SDK events: `message/`, `receipt/`, `user/`, `group/`, `call/`, `connection/`, `conversation/` |
  | UI event prefix | `ui:message/`, `ui:compose/`, `ui:group/`, `ui:call/`, `ui:thread/`, `ui:conversation/`, `ui:card/` |
</Accordion>

## Overview

The v7 event system merges CometChat SDK listener events (from the network) with local UI events (from component actions) into a single pub/sub bus. Components subscribe to events they care about and publish events when they perform actions that other components need to know about.

This replaces v6's RxJS-based `CometChatMessageEvents`, `CometChatGroupEvents`, etc. with a single unified system.

***

## How It Works

```mermaid theme={null}
flowchart LR
  subgraph CometChatEventsProvider
    SDK["SDK Listeners\n(message, user, group, call, connection)"]
    UI["UI Events\n(publish())"]
    Bus["Event Bus"]
    Subs["subscribers.forEach()"]

    SDK --> Bus
    UI --> Bus
    Bus --> Subs
  end
```

1. **SDK listeners** are attached when `CometChatEventsProvider` mounts (after login)
2. When the SDK fires a listener callback, it's converted to a typed event and emitted to all subscribers
3. Components can also **publish** UI events for local cross-component communication
4. All subscribers receive all events — filter by `event.type` in your handler

***

## Subscribing to Events

Use the `useCometChatEvents` hook to subscribe:

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

function MyComponent() {
  useCometChatEvents((event) => {
    switch (event.type) {
      case "message/text-received":
        console.log("New message:", event.message.getText());
        break;
      case "user/online":
        console.log("User online:", event.user.getName());
        break;
      case "ui:message/sent":
        // Fires once per send stage — filter to avoid handling the same message twice.
        if (event.status === "success") {
          console.log("Message sent:", event.message.getId());
        }
        break;
    }
  }, []);

  return <div>...</div>;
}
```

The hook automatically subscribes on mount and unsubscribes on unmount. No cleanup needed.

<Warning>
  **`deps` is a required second argument.** It is not optional.

  Pass `[]` when the handler closes over nothing that changes, exactly as you would for `useEffect`. The handler reference itself is held in a ref and stays current on every render, so it never needs to appear in `deps` — list only the external values the handler reads, such as a conversation ID.

  ```tsx theme={null}
  useCometChatEvents((event) => {
    if (event.type === "message/text-received" && event.message.getReceiverId() === conversationId) {
      // ...
    }
  }, [conversationId]);
  ```
</Warning>

***

## Publishing UI Events

Use the `usePublishEvent` hook to publish events that other components can react to:

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

function MyComponent() {
  const publish = usePublishEvent();

  const handleSend = (message: CometChat.BaseMessage) => {
    publish({
      type: "ui:message/sent",
      message,
      status: CometChatMessageStatus.success,
    });
  };

  return <button onClick={() => handleSend(msg)}>Send</button>;
}
```

Only `ui:` prefixed events can be published by components. SDK events are emitted internally by the provider.

***

## SDK Events

These events originate from the CometChat SDK (network). They fire when other users perform actions.

### Message Events

| Event Type | Payload | When |
| - | - | - |
| `message/text-received` | `{ message: TextMessage }` | Text message received |
| `message/media-received` | `{ message: MediaMessage }` | Media message received |
| `message/custom-received` | `{ message: CustomMessage }` | Custom message received |
| `message/interactive-received` | `{ message: InteractiveMessage }` | Interactive message received |
| `message/card-received` | `{ message: BaseMessage }` | Card message (`category: "card"`) received |
| `message/ai-assistant-received` | `{ message: BaseMessage }` | AI assistant message received |
| `message/edited` | `{ message: BaseMessage }` | Message was edited |
| `message/deleted` | `{ message: BaseMessage }` | Message was deleted |
| `message/moderated` | `{ message: BaseMessage }` | Message was moderated |

### Pin and Save

| Event Type | Payload | When |
| - | - | - |
| `message/pinned` | `{ message: BaseMessage }` | A message was pinned in the conversation |
| `message/unpinned` | `{ message: BaseMessage }` | A message was unpinned |
| `message/saved` | `{ message: BaseMessage }` | The current user saved a message |
| `message/unsaved` | `{ message: BaseMessage }` | The current user unsaved a message |

`CometChatPinnedMessages` and `CometChatSavedMessages` subscribe to these to keep their lists in sync in real time. See the [Pin & Save Messages guide](/ui-kit/react/guide-pin-and-save-messages).

### Conversation Events

| Event Type | Payload | When |
| - | - | - |
| `conversation/pinned` | `{ conversation: Conversation }` | A conversation was pinned (from the user's other device) |
| `conversation/unpinned` | `{ conversation: Conversation }` | A conversation was unpinned |

`CometChatConversations` subscribes to these to re-order the list in real time. Pinning a conversation from the current tab is reflected optimistically through the `ui:conversation/pin-changed` UI event below; these SDK events carry a change made elsewhere. See [Conversations → Pin Conversation](/ui-kit/react/components/conversations#pin-conversation).

### Receipt Events

| Event Type | Payload | When |
| - | - | - |
| `receipt/delivered` | `{ receipt: MessageReceipt }` | Message delivered to recipient |
| `receipt/read` | `{ receipt: MessageReceipt }` | Message read by recipient |
| `receipt/delivered-to-all` | `{ receipt: MessageReceipt }` | Message delivered to all (group) |
| `receipt/read-by-all` | `{ receipt: MessageReceipt }` | Message read by all (group) |

### Reaction Events

| Event Type | Payload | When |
| - | - | - |
| `reaction/added` | `{ event: ReactionEvent }` | Reaction added to a message |
| `reaction/removed` | `{ event: ReactionEvent }` | Reaction removed from a message |

### Typing Events

| Event Type | Payload | When |
| - | - | - |
| `typing/started` | `{ indicator: TypingIndicator }` | User started typing |
| `typing/ended` | `{ indicator: TypingIndicator }` | User stopped typing |

### User Events

| Event Type | Payload | When |
| - | - | - |
| `user/online` | `{ user: User }` | User came online |
| `user/offline` | `{ user: User }` | User went offline |

### Group Events

| Event Type | Payload | When |
| - | - | - |
| `group/member-joined` | `{ action, joinedUser, joinedGroup }` | Member joined a group |
| `group/member-left` | `{ action, leftUser, leftGroup }` | Member left a group |
| `group/member-kicked` | `{ action, kickedUser, kickedBy, kickedFrom }` | Member was kicked |
| `group/member-banned` | `{ action, bannedUser, bannedBy, bannedFrom }` | Member was banned |
| `group/member-unbanned` | `{ action, unbannedUser, unbannedBy, unbannedFrom }` | Member was unbanned |
| `group/member-added` | `{ action, addedBy, addedUser, addedTo }` | Member was added |
| `group/member-scope-changed` | `{ action, changedUser, newScope, oldScope, changedGroup }` | Member scope changed |

### Call Events

| Event Type | Payload | When |
| - | - | - |
| `call/incoming` | `{ call: Call }` | Incoming call received |
| `call/accepted` | `{ call: Call }` | Outgoing call was accepted |
| `call/rejected` | `{ call: Call }` | Outgoing call was rejected |
| `call/cancelled` | `{ call: Call }` | Incoming call was cancelled |
| `call/ended` | `{ call: Call }` | Call ended |

### Connection Events

| Event Type | Payload | When |
| - | - | - |
| `connection/connected` | — | WebSocket connected |
| `connection/disconnected` | — | WebSocket disconnected |

***

## UI Events

These events are published by UI Kit components for local cross-component communication within the same tab.

### Message Lifecycle

| Event Type | Payload | Published by |
| - | - | - |
| `ui:message/sent` | `{ message, status }` | MessageComposer |
| `ui:message/deleted` | `{ message }` | MessageList (after delete) |
| `ui:message/read` | `{ message }` | MessageList (mark as read) |

<Warning>
  **`ui:message/sent` fires more than once per message.** It is emitted at each stage of the send, so a handler that does not filter on `status` runs more than once for every message:

  | `status` | When | `message` |
  | - | - | - |
  | `"inprogress"` | Optimistically, before the SDK confirms | The local object — **`getId()` is still undefined** |
  | `"success"` | After the SDK confirms the send | The server's message, with its real ID |
  | `"error"` | The send failed | The local object, with `error` in its metadata |

  Filter on the stage you care about — `status === "success"` for a confirmed send:

  ```tsx theme={null}
  useCometChatEvents((event) => {
    if (event.type === "ui:message/sent" && event.status === "success") {
      analytics.track("message_sent", { id: event.message.getId() });
    }
  }, []);
  ```

  v6's `ccMessageSent` fired at the same three stages, so the staging itself is not new. What changed is the type of `status`: v6's `MessageStatus` was a **numeric** enum (`success = 1`) and is gone in v7, replaced by `CometChatMessageStatus`, whose values are **strings** (`"success"`).

  | v6 filter | Porting to v7 |
  | - | - |
  | `status === MessageStatus.success` | Fails loudly — `MessageStatus` is not exported in v7, so TypeScript errors and JavaScript throws |
  | `status === 1` | TypeScript flags it (`TS2367` — `CometChatMessageStatus` and `number` have no overlap). In plain JavaScript it compiles and runs but never matches again, so the handler quietly stops firing |

  Change either to `status === "success"`, or import `CometChatMessageStatus` and compare with `CometChatMessageStatus.success`.
</Warning>

### Pin and Save (optimistic)

Published by the message options the moment a pin/save succeeds locally, before the network echo. Pinned/Saved panels and the message list listen to these so every surface flips together.

| Event Type | Payload | Published by |
| - | - | - |
| `ui:message/pin-changed` | `{ message, pinned }` | Message options (pin / unpin action) |
| `ui:message/save-changed` | `{ message, saved }` | Message options (save / unsave action) |

### Composer Commands

| Event Type | Payload | Published by |
| - | - | - |
| `ui:compose/edit` | `{ message, status, parentMessageId? }` | MessageList (edit option) |
| `ui:compose/reply` | `{ message, status, parentMessageId? }` | MessageList (reply option) |
| `ui:compose/text` | `{ text }` | Smart replies, conversation starters |
| `ui:compose/recording-started` | `{ composerInstanceId }` | MessageComposer (voice recording) |

### Conversation State

| Event Type | Payload | Published by |
| - | - | - |
| `ui:conversation/read` | `{ conversationId }` | MessageList |
| `ui:conversation/updated` | `{ conversation }` | MessageList |
| `ui:conversation/deleted` | `{ conversation }` | Conversations (delete action) |
| `ui:conversation/pin-changed` | `{ conversation, pinned }` | Conversations (pin / unpin action; optimistic re-order) |
| `ui:active-chat/changed` | `{ user?, group?, message?, unreadMessageCount? }` | MessageList (on load) |

### User & Group Actions

| Event Type | Payload | Published by |
| - | - | - |
| `ui:user/blocked` | `{ user }` | MessageHeader |
| `ui:user/unblocked` | `{ user }` | MessageHeader |
| `ui:group/created` | `{ group }` | Groups |
| `ui:group/left` | `{ group }` | GroupMembers |
| `ui:group/deleted` | `{ group }` | Groups |
| `ui:group/member-kicked` | `{ message, user, group }` | GroupMembers |
| `ui:group/member-banned` | `{ message, user, group }` | GroupMembers |
| `ui:group/member-unbanned` | `{ message?, user, group }` | GroupMembers |
| `ui:group/member-scope-changed` | `{ message, user, group, newScope }` | GroupMembers |
| `ui:group/ownership-changed` | `{ group, newOwner, previousOwnerUid }` | GroupMembers |

**Used by:** the [Block/Unblock User guide](/ui-kit/react/guide-block-unblock-user) publishes and subscribes to `ui:user/blocked` / `ui:user/unblocked` to keep the composer in sync, and the [Group Chat Setup guide](/ui-kit/react/guide-group-chat-setup) reacts to `ui:group/created` in the group creation flow.

### Thread

| Event Type | Payload | Published by |
| - | - | - |
| `ui:thread/opened` | `{ parentMessage }` | MessageList (thread option) |
| `ui:thread/closed` | — | ThreadHeader |
| `ui:thread/subscription-changed` | `{ parentMessageId, subscribed }` | ThreadHeader / MessageList (optimistic subscribe-unsubscribe flip) |

**Used by:** the [Threaded Messages guide](/ui-kit/react/guide-threaded-messages) opens and closes the thread panel in response to `ui:thread/opened` / `ui:thread/closed`.

### Call Actions

| Event Type | Payload | Published by |
| - | - | - |
| `ui:call/outgoing` | `{ call }` | MessageHeader (call buttons) |
| `ui:call/rejected` | `{ call }` | IncomingCall |
| `ui:call/ended` | `{ call? }` | OngoingCall |
| `ui:call/accepted` | `{ call }` | IncomingCall |
| `ui:call/join` | `{ sessionId, message }` | CallLogs |

### Navigation

| Event Type | Payload | Published by |
| - | - | - |
| `ui:open-chat` | `{ user?, group? }` | MessageList (message privately option) |

**Used by:** the [Message Privately guide](/ui-kit/react/guide-message-privately) subscribes to `ui:open-chat` to open a private one-on-one panel from within a group chat.

### Card Actions

| Event Type | Payload | Published by |
| - | - | - |
| `ui:card/action` | `{ message, action }` | [Card Bubble](/ui-kit/react/components/card-bubble) (user clicks a card action) |

Published when a user clicks an action (button, link, etc.) inside a card message bubble. The kit performs no behavior — it forwards the raw `action` and its `message` to your app, so you own all action handling. This is the only channel that can reach a kit-instantiated card (e.g. a nested agent card) where no prop is reachable.

### Panels

| Event Type | Payload | Published by |
| - | - | - |
| `ui:panel/show` | `{ position, panel }` | AI features |
| `ui:panel/hide` | `{ position }` | AI features |

***

## Differences from v6

| v6 | v7 |
| - | - |
| Multiple RxJS Subject classes (`CometChatMessageEvents`, `CometChatGroupEvents`, etc.) | Single unified event bus |
| `CometChatMessageEvents.ccMessageSent.next(...)` | `publish({ type: 'ui:message/sent', ... })` |
| `CometChatMessageEvents.ccMessageSent.subscribe(...)` | `useCometChatEvents((e) => { if (e.type === 'ui:message/sent' && e.status === 'success') ... }, [])` |
| Manual listener management | Automatic — provider handles all SDK listeners |
| RxJS dependency | No external dependencies |


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