> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Card Messages

> How the React UI Kit renders Card Messages — developer cards, persisted agent cards, and streaming agent cards — and how to handle card actions.

<Accordion title="AI Integration Quick Reference">
  | 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) |
</Accordion>

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.

<Info>
  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.
</Info>

## 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 theme={null}
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 theme={null}
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 theme={null}
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 theme={null}
function App() {
  useCardActions();
  return <YourChatUI />;
}
```

<Warning>
  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.
</Warning>

### `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.
