Skip to main content
FieldValue
Package@cometchat/chat-uikit-react
Renderer@cometchat/cards-react (CometChatCardView)
ComponentsCometChatCardBubble, CometChatAIAssistantMessageBubble, CometChatStreamMessageBubble
Key eventCometChatUIEvents.ccCardActionClicked
Required setupCometChatUIKit.init(uiKitSettings) then CometChatUIKit.login("UID")
PurposeRender structured, interactive card payloads inside conversations and forward card actions to your app
RelatedCard Bubble | AI Assistant Chat | Events
Card Messages are structured, interactive cards delivered inside conversations. The React UI Kit renders them through the prebuilt @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.
PathSourceComponentRouted by
Developer cardCardMessage with category: "card"CometChatCardBubblemessage category
Persisted agent cardAIAssistantMessage content blockCometChatAIAssistantMessageBubbleelement type: "card"
Streaming agent cardLive card_start / card / card_end eventsCometChatStreamMessageBubblestream event type

Developer cards

A message with category "card" is routed to CometChatCardBubble by CometChatMessageList’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:
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 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 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 eventBehavior
card_startShows an in-place loader labeled with the event’s executionText, keyed by cardId.
cardReplaces the loader (correlated by cardId) with the rendered card.
card_endNo-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

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:
typeIntended behavior
openUrlOpen a URL (in a new tab or a webview).
copyToClipboardCopy a value to the clipboard.
downloadFileDownload a file.
sendMessageSend a text message to a user/group (or the current conversation).
apiCallMake an HTTP request.
chatWithUserOpen a one-to-one chat with a user.
chatWithGroupOpen a group chat.
initiateCallStart an audio/video call.
customCallbackInvoke 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:
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:
function App() {
  useCardActions();
  return <YourChatUI />;
}
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 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.
  • Card Bubble — the developer card component reference.
  • AI Assistant Chat — the agent chat experience that renders persisted and streaming agent cards.
  • EventsccCardActionClicked and onCardMessageReceived reference.