diff --git a/docs.json b/docs.json
index daec3b436..7525187b6 100644
--- a/docs.json
+++ b/docs.json
@@ -710,6 +710,7 @@
"ui-kit/react/v6/call-logs",
"ui-kit/react/v6/search",
"ui-kit/react/v6/ai-assistant-chat",
+ "ui-kit/react/v6/card-bubble",
"ui-kit/react/v6/notification-feed"
]
},
@@ -731,6 +732,7 @@
"ui-kit/react/v6/guide-search-messages",
"ui-kit/react/v6/guide-call-log-details",
"ui-kit/react/v6/guide-group-chat",
+ "ui-kit/react/v6/card-messages",
"ui-kit/react/v6/custom-text-formatter-guide",
"ui-kit/react/v6/mentions-formatter-guide",
"ui-kit/react/v6/url-formatter-guide",
diff --git a/ui-kit/react/v6/card-bubble.mdx b/ui-kit/react/v6/card-bubble.mdx
new file mode 100644
index 000000000..501aac081
--- /dev/null
+++ b/ui-kit/react/v6/card-bubble.mdx
@@ -0,0 +1,198 @@
+---
+title: "Card Bubble"
+description: "React component that renders a developer Card Message inside a message bubble using the prebuilt CometChat Cards renderer, with pure-forwarded card actions."
+---
+
+
+```json
+{
+ "component": "CometChatCardBubble",
+ "kind": "content-view-component",
+ "package": "@cometchat/chat-uikit-react",
+ "import": "import { CometChatCardBubble } from \"@cometchat/chat-uikit-react\";",
+ "renderer": "@cometchat/cards-react (CometChatCardView)",
+ "description": "Render-only bubble for developer Card Messages (category: \"card\"). Serializes the raw card payload verbatim and hands it to the prebuilt Cards renderer. Forwards card actions untouched — implements no action behavior.",
+ "usage": "Rendered automatically by CometChatMessageList for any message with category \"card\". Instantiate directly only in a fully custom message renderer.",
+ "props": {
+ "message": { "type": "CometChat.BaseMessage", "required": false, "description": "The card message. Raw payload is read from getCard() → data.card." },
+ "cardJson": { "type": "string", "default": "derived from message", "description": "Raw card JSON to render directly. Used for nested agent-card blocks." },
+ "isSentByMe": { "type": "boolean", "default": "true", "description": "Toggles outgoing/incoming styling." },
+ "onCardAction": { "type": "(message, action) => void", "default": "undefined", "description": "Optional per-instance action callback. The bubble also always emits ccCardActionClicked on the UI event bus." }
+ },
+ "eventBus": "CometChatUIEvents.ccCardActionClicked",
+ "relatedComponents": ["CometChatMessageList", "CometChatMessageTemplate", "CometChatAIAssistantChat"],
+ "cssRootClass": "cometchat-card-bubble"
+}
+```
+
+
+The `CometChatCardBubble` component renders a **developer Card Message** (`message.getCategory() === "card"`) as a card bubble inside a conversation. It is the card equivalent of the text bubble: the surrounding [`CometChatMessageList`](/ui-kit/react/v6/message-list) bubble supplies the container, receipts, reactions, options, reply and thread view — this component only replaces the **content view** with the rendered card.
+
+## Overview
+
+The UI Kit is a **render-only** consumer of cards: it draws cards delivered by the SDK and forwards card actions to your app. It never parses or mutates the card body, and never sends or creates cards. When a card message arrives, the UI Kit routes it to this bubble automatically.
+
+The component follows a strict **render-only contract**:
+
+- **Render-only** — the raw card payload from `message.getCard()` (or the raw `data.card`) is serialized verbatim and handed to the prebuilt `CometChatCardView` renderer (from [`@cometchat/cards-react`](https://www.npmjs.com/package/@cometchat/cards-react)) as a `cardJson` string. The UI Kit performs **zero transformation** of the payload.
+- **No behavior** — the bubble runs no action logic of its own. When a user taps an interactive element, the renderer's raw action is **pure-forwarded** on two channels (see [Card Actions](#card-actions)); your app owns all behavior.
+- **Graceful fallback** — when the payload is empty or invalid, a single-line fallback text is shown instead of an empty bubble (see [Fallback Behavior](#fallback-behavior)).
+
+
+ Card rendering (layout, theming, interactive elements) is owned by the prebuilt `@cometchat/cards-react` renderer library, **not** by the UI Kit. The UI Kit is responsible only for delivering the payload to the renderer and forwarding actions back out.
+
+
+## Automatic Rendering
+
+In the standard chat flow you do **not** instantiate this component yourself. [`CometChatMessageList`](/ui-kit/react/v6/message-list) routes any message with category `"card"` to `CometChatCardBubble` automatically, keyed by **category** (the developer `type` is arbitrary). To handle card actions in this flow, subscribe to the [`ccCardActionClicked`](#card-actions) event bus — no component wiring is required.
+
+Use the component directly only when you are building a fully custom message renderer.
+
+## Basic Usage
+
+```tsx
+import { CometChatCardBubble } from "@cometchat/chat-uikit-react";
+
+function CardMessage({ cardMessage }: { cardMessage: CometChat.BaseMessage }) {
+ return ;
+}
+```
+
+### Incoming vs Outgoing Messages
+
+`isSentByMe` toggles the outgoing (sender) vs incoming (receiver) styling, mirroring the text bubble.
+
+```tsx
+import { CometChatCardBubble } from "@cometchat/chat-uikit-react";
+
+function CardList({
+ incomingCard,
+ outgoingCard,
+}: {
+ incomingCard: CometChat.BaseMessage;
+ outgoingCard: CometChat.BaseMessage;
+}) {
+ return (
+ <>
+ {/* Incoming card (left-aligned) */}
+
+
+ {/* Outgoing card (right-aligned) */}
+
+ >
+ );
+}
+```
+
+## Props
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `message` | `CometChat.BaseMessage` | — | The card message to render. The raw payload is read from `message.getCard()`, falling back to `message.getData().card`. Used both to draw the card and to tag forwarded actions. |
+| `cardJson` | `string` | derived from `message` | Raw card JSON to render directly. When omitted, it is derived from `message`. Used for nested agent-card blocks where the payload is already extracted. |
+| `isSentByMe` | `boolean` | `true` | Toggles outgoing/incoming styling. `false` for incoming/receiver messages, `true` for outgoing/sender messages. |
+| `onCardAction` | `(message: CometChat.BaseMessage \| undefined, action: any) => void` | `undefined` | Optional per-instance action callback for apps that render this bubble directly. Carries the message and the renderer's raw action. |
+
+
+ The renderer theme is resolved automatically from `CometChatUIKit.themeMode` — there is no `themeMode` prop. To restyle the rendered card, theme the `@cometchat/cards-react` renderer directly.
+
+
+## Card Actions
+
+The Cards renderer is a **pure renderer** — it emits actions through a callback without executing them. The bubble forwards each action on **both** channels and runs no behavior of its own:
+
+1. **`onCardAction` prop** — for apps that render this bubble directly.
+2. **`CometChatUIEvents.ccCardActionClicked` event bus** — for the standard, internally rendered flow where the bubble is created by the UI Kit. This is the recommended channel for the default message list.
+
+
+ Act on **one** channel only to avoid double-handling the same tap. In the standard `CometChatMessageList` flow, use the `ccCardActionClicked` bus.
+
+
+### Handling actions via the prop
+
+```tsx
+import { CometChatCardBubble } from "@cometchat/chat-uikit-react";
+
+function CardMessage({ cardMessage }: { cardMessage: CometChat.BaseMessage }) {
+ const onCardAction = (
+ message: CometChat.BaseMessage | undefined,
+ action: any
+ ) => {
+ // `action` is the raw renderer action.
+ // Your app implements the behavior — the UI Kit performs none.
+ console.log("Card action on message", message?.getId(), action);
+ };
+
+ return ;
+}
+```
+
+### Handling actions via the event bus
+
+```tsx
+import { useEffect } from "react";
+import { CometChatUIEvents } from "@cometchat/chat-uikit-react";
+
+function useCardActions() {
+ useEffect(() => {
+ const sub = CometChatUIEvents.ccCardActionClicked.subscribe(({ message, action }) => {
+ switch (action?.type) {
+ case "openUrl":
+ window.open(action.url, "_blank", "noopener,noreferrer");
+ break;
+ case "copyToClipboard":
+ navigator.clipboard?.writeText(action.value);
+ break;
+ // ...handle the remaining action types
+ }
+ });
+ return () => sub.unsubscribe();
+ }, []);
+}
+```
+
+The full set of action types and a complete reference handler are covered in the [Card Messages guide](/ui-kit/react/v6/card-messages#handling-card-actions).
+
+## Fallback Behavior
+
+When the payload is empty or invalid (`null`, a blank string, or an empty object), the bubble renders a single line of fallback text instead of an empty card. The fallback is resolved in this order:
+
+1. `message.getFallbackText()`
+2. `message.getText()`
+3. Localized `"Card Message"` (the `card_message` localization key)
+
+This keeps the conversation readable even if a card payload is malformed or a client cannot render it.
+
+## Customization
+
+The card's internal layout, colors, and typography are controlled by the `@cometchat/cards-react` renderer. The bubble wrapper and fallback line are styled with CSS variables:
+
+```css
+.cometchat-card-bubble {
+ /* Bubble surface (incoming) */
+ --cometchat-background-color-02: #f5f5f5;
+ --cometchat-radius-3: 12px;
+}
+
+.cometchat-card-bubble__fallback {
+ /* Fallback text */
+ --cometchat-font-body-regular: 400 14px "Inter";
+ --cometchat-text-color-secondary: #666666;
+}
+```
+
+To restyle the rendered card itself (button colors, header, body), theme the renderer rather than overriding CSS — the card DOM is owned by `@cometchat/cards-react`.
+
+## Technical Details
+
+- **Content-view component** — rendered as the `contentView` of the developer card message template, so it inherits the full bubble (status info, reply view, reactions, options).
+- **Renderer dependency** — `CometChatCardView` from `@cometchat/cards-react`.
+- **Callback before schema** — the renderer's `onAction` callback is bound before `cardJson` is rendered, so actions are captured from the first tap.
+- **Theme** — the renderer `themeMode` is taken from `CometChatUIKit.themeMode`.
+
+## Related
+
+- **[Card Messages guide](/ui-kit/react/v6/card-messages)** — the full card rendering implementation: developer cards, agent cards, streaming cards, and the complete action vocabulary.
+- **[CometChatMessageList](/ui-kit/react/v6/message-list)** — routes card messages to this bubble.
+- **[CometChatMessageTemplate](/ui-kit/react/v6/message-template)** — the template that maps `category: "card"` to this bubble.
+- **[Events](/ui-kit/react/v6/events)** — the `ccCardActionClicked` event reference.
diff --git a/ui-kit/react/v6/card-messages.mdx b/ui-kit/react/v6/card-messages.mdx
new file mode 100644
index 000000000..aaf22a00f
--- /dev/null
+++ b/ui-kit/react/v6/card-messages.mdx
@@ -0,0 +1,199 @@
+---
+title: "Card Messages"
+description: "How the React UI Kit renders Card Messages — developer cards, persisted agent cards, and streaming agent cards — and how to handle card actions."
+---
+
+
+
+| Field | Value |
+| --- | --- |
+| Package | `@cometchat/chat-uikit-react` |
+| Renderer | `@cometchat/cards-react` (`CometChatCardView`) |
+| Components | `CometChatCardBubble`, `CometChatAIAssistantMessageBubble`, `CometChatStreamMessageBubble` |
+| Key event | `CometChatUIEvents.ccCardActionClicked` |
+| Required setup | `CometChatUIKit.init(uiKitSettings)` then `CometChatUIKit.login("UID")` |
+| Purpose | Render structured, interactive card payloads inside conversations and forward card actions to your app |
+| Related | [Card Bubble](/ui-kit/react/v6/card-bubble) \| [AI Assistant Chat](/ui-kit/react/v6/ai-assistant-chat) \| [Events](/ui-kit/react/v6/events) |
+
+
+
+Card Messages are structured, interactive cards delivered inside conversations. The React UI Kit renders them through the prebuilt [`@cometchat/cards-react`](https://www.npmjs.com/package/@cometchat/cards-react) renderer and forwards every card action back to your application.
+
+
+ The UI Kit is a **render-only** consumer of cards: it draws cards delivered by the SDK and forwards their actions to your app. It never parses or mutates the card body, and never sends or creates cards.
+
+
+## The render-only contract
+
+The UI Kit is a **render-only** consumer of cards. For every card surface, the rules are identical:
+
+1. **Pass the payload through unchanged.** The raw card payload is read from the SDK, serialized verbatim (`JSON.stringify`), and handed to `CometChatCardView` as a `cardJson` string. The UI Kit performs **no transformation**.
+2. **Forward actions, run no behavior.** The renderer emits actions through a callback. The UI Kit forwards them on the `ccCardActionClicked` event bus. Your app implements **all** behavior (open a URL, start a chat, call an API, …).
+3. **Fall back gracefully.** When a payload is empty or invalid, a single fallback line is shown instead of a broken card.
+
+The developer bubble, the agent bubble, and the streaming bubble all treat payloads identically.
+
+## Three delivery paths
+
+A card can reach the conversation in three ways. The UI Kit routes each one to the correct renderer automatically.
+
+| Path | Source | Component | Routed by |
+| --- | --- | --- | --- |
+| **Developer card** | `CardMessage` with `category: "card"` | [`CometChatCardBubble`](/ui-kit/react/v6/card-bubble) | message **category** |
+| **Persisted agent card** | `AIAssistantMessage` content block | `CometChatAIAssistantMessageBubble` | element `type: "card"` |
+| **Streaming agent card** | Live `card_start` / `card` / `card_end` events | `CometChatStreamMessageBubble` | stream event type |
+
+### Developer cards
+
+A message with category `"card"` is routed to `CometChatCardBubble` by [`CometChatMessageList`](/ui-kit/react/v6/message-list)'s message template, keyed on **category** (the developer `type` is arbitrary). The bubble renders `message.getCard()` and forwards taps on the `ccCardActionClicked` bus.
+
+In the conversation list, a card message's preview is its `getText()` if present, otherwise the localized `"Card Message"` label.
+
+To react to incoming developer cards in real time, listen on the SDK message bus:
+
+```tsx
+import { useEffect } from "react";
+import { CometChatMessageEvents } from "@cometchat/chat-uikit-react";
+
+function useCardListener() {
+ useEffect(() => {
+ const sub = CometChatMessageEvents.onCardMessageReceived.subscribe(
+ (card: CometChat.InteractiveMessage) => {
+ console.log("Card received:", card.getId(), (card as any).getCard?.());
+ }
+ );
+ return () => sub.unsubscribe();
+ }, []);
+}
+```
+
+See the [Card Bubble](/ui-kit/react/v6/card-bubble) component reference for props, actions, and theming.
+
+### Persisted agent cards
+
+After an AI agent run completes, the persisted `AIAssistantMessage` exposes its content as an **ordered list of blocks** via `getElements()`. `CometChatAIAssistantMessageBubble` renders them in order:
+
+- a `text` block renders as Markdown (via `react-markdown`);
+- a `card` block renders through `CometChatCardView`, using the same render-only path as developer cards.
+
+When a message has no elements (older messages), the bubble falls back to `getText()`. No additional wiring is required — the [AI Assistant Chat](/ui-kit/react/v6/ai-assistant-chat) flow instantiates this bubble for you. Taps on a nested agent card are forwarded on the same `ccCardActionClicked` bus.
+
+### Streaming agent cards
+
+While an agent run is streaming, cards arrive progressively and are rendered by `CometChatStreamMessageBubble`, which subscribes to the streaming service's `messageStream`:
+
+| Stream event | Behavior |
+| --- | --- |
+| `card_start` | Shows an in-place loader labeled with the event's `executionText`, keyed by `cardId`. |
+| `card` | Replaces the loader (correlated by `cardId`) with the rendered card. |
+| `card_end` | No-op — the run-complete persisted `AIAssistantMessage` replaces the streamed bubble. |
+
+Because no persisted message exists yet during streaming, a tap on a streaming card is forwarded without an owning message on the `ccCardActionClicked` bus; the persisted bubble that follows is the source of truth.
+
+## Handling card actions
+
+The Cards renderer emits actions but never executes them. The UI Kit forwards each action **untouched** on `CometChatUIEvents.ccCardActionClicked`. Subscribe **once** at app startup and dispatch by action type.
+
+### The event payload
+
+```typescript
+export interface ICardActionClicked {
+ /** The owning message — CardMessage (developer) or AIAssistantMessage (persisted agent card). */
+ message: CometChat.BaseMessage;
+ /** The renderer's raw action object. The kit never interprets it. */
+ action: any;
+}
+```
+
+### Action types
+
+`action` is the raw action object emitted by `@cometchat/cards-react`, narrowed by its `type`. The UI Kit forwards all nine of them; your app decides what each one does:
+
+| `type` | Intended behavior |
+| --- | --- |
+| `openUrl` | Open a URL (in a new tab or a webview). |
+| `copyToClipboard` | Copy a value to the clipboard. |
+| `downloadFile` | Download a file. |
+| `sendMessage` | Send a text message to a user/group (or the current conversation). |
+| `apiCall` | Make an HTTP request. |
+| `chatWithUser` | Open a one-to-one chat with a user. |
+| `chatWithGroup` | Open a group chat. |
+| `initiateCall` | Start an audio/video call. |
+| `customCallback` | Invoke an app-defined handler, keyed by `callbackId`. |
+
+### Reference handler
+
+Subscribe once — a top-level hook mounted at your app shell is the natural home — and dispatch by `type`:
+
+```tsx
+import { useEffect } from "react";
+import { CometChat } from "@cometchat/chat-sdk-javascript";
+import { CometChatUIEvents } from "@cometchat/chat-uikit-react";
+
+export function useCardActions() {
+ useEffect(() => {
+ const sub = CometChatUIEvents.ccCardActionClicked.subscribe(({ message, action }) => {
+ if (!action || typeof action !== "object") return;
+
+ switch (action.type) {
+ case "openUrl": {
+ if (!action.url) return;
+ const target = action.openIn === "webview" ? "_self" : "_blank";
+ window.open(action.url, target, "noopener,noreferrer");
+ break;
+ }
+ case "copyToClipboard": {
+ if (action.value != null) navigator.clipboard?.writeText(action.value);
+ break;
+ }
+ case "chatWithUser": {
+ if (!action.uid) return;
+ CometChat.getUser(action.uid).then((user) => {
+ // Navigate your app to the conversation with `user`.
+ });
+ break;
+ }
+ // ...handle downloadFile, sendMessage, apiCall, chatWithGroup,
+ // initiateCall, and customCallback the same way.
+ default:
+ break; // Unknown action — the renderer owns the action vocabulary.
+ }
+ });
+
+ return () => sub.unsubscribe();
+ }, []);
+}
+```
+
+Call the hook once from your app shell so it subscribes exactly once:
+
+```tsx
+function App() {
+ useCardActions();
+ return ;
+}
+```
+
+
+ Each tap is forwarded on **one** bus to all subscribers. Subscribe in a single place so an action runs exactly once. If you also bind the [`onCardAction` prop](/ui-kit/react/v6/card-bubble#card-actions) on a directly-rendered card bubble, handle the action on only one of the two channels.
+
+
+### `customCallback` actions
+
+A `customCallback` action carries a `callbackId` (and optional `payload`). Map each `callbackId` to an app-defined handler, and use the action's element context and `message?.getId()` for context. No server message is sent by the UI Kit for a custom callback — it is entirely app-defined.
+
+## Fallback behavior
+
+Every card surface resolves a single-line fallback when the payload is empty or invalid:
+
+1. `getFallbackText()` (when available on the message/element)
+2. `getText()`
+3. Localized `"Card Message"`
+
+This guarantees a readable conversation even when a card cannot be rendered.
+
+## Related
+
+- **[Card Bubble](/ui-kit/react/v6/card-bubble)** — the developer card component reference.
+- **[AI Assistant Chat](/ui-kit/react/v6/ai-assistant-chat)** — the agent chat experience that renders persisted and streaming agent cards.
+- **[Events](/ui-kit/react/v6/events)** — `ccCardActionClicked` and `onCardMessageReceived` reference.
diff --git a/ui-kit/react/v6/events.mdx b/ui-kit/react/v6/events.mdx
index b00bb4989..c310c4c77 100644
--- a/ui-kit/react/v6/events.mdx
+++ b/ui-kit/react/v6/events.mdx
@@ -77,6 +77,7 @@ Events provide decoupled communication between UI Kit components. Components emi
| **onMessageEdited** | This event is emitted when the CometChat SDK listener indicates that a message has been edited. |
| **onMessageDeleted** | This event is emitted when the CometChat SDK listener indicates that a message has been deleted. |
| **onTransientMessageReceived** | This event is emitted when the CometChat SDK listener emits a transient message. |
+| **onCardMessageReceived** | This event is emitted when the CometChat SDK listener emits a developer card message (`category: "card"`). See [Card Messages](/ui-kit/react/v6/card-messages#developer-cards). |
### CometChatCallEvents
@@ -96,3 +97,4 @@ UI events are triggered when a user interacts with UI Kit elements such as butto
| Name | Description |
| ------------------- | ---------------------------------------------------------------------------- |
| **ccActiveChatChanged** | This event is triggered when the user navigates to a particular chat window. |
+| **ccCardActionClicked** | This event is triggered when a user taps an interactive element on a rendered card. The UI Kit forwards the raw renderer action untouched; your app implements the behavior. See [Card Messages](/ui-kit/react/v6/card-messages#handling-card-actions). |