| name | x-card |
| version | 2.9.0 |
| description | Use when building AI-driven UIs with @ant-design/x-card — covers XCard.Box, XCard.Card, A2UI v0.9 commands, data binding, catalogs, actions, and streaming patterns. |
🎯 Skill Positioning
This skill covers @ant-design/x-card — the React implementation of the A2UI protocol, enabling AI agents to dynamically render rich interactive UIs through structured JSON command streams.
It covers:
XCard.Box + XCard.Card component usage
- A2UI v0.9 command types:
createSurface, updateComponents, updateDataModel, deleteSurface
- Custom component registration and catalog management
- Data binding via JSON Pointer paths (RFC 6901)
- Action handling — sending user events back to the agent
- Streaming progressive rendering patterns
- v0.8 ↔ v0.9 protocol differences
Scope: v0.9 is the recommended protocol. v0.8 is supported for backward compatibility only — prefer v0.9 for all new work.
Table of Contents
📦 Package Overview
| Package | Responsibility |
|---|
@ant-design/x-card | React renderer for A2UI protocol — XCard.Box, XCard.Card, catalog APIs |
@ant-design/x | Chat UI components (Bubble, Sender, etc.) — not covered here |
@ant-design/x-sdk | Data providers, streaming — not covered here |
npm install @ant-design/x-card
Exports:
import {
XCard,
registerCatalog,
loadCatalog,
validateComponent,
clearCatalogCache,
} from '@ant-design/x-card';
import type {
XAgentCommand_v0_9,
XAgentCommand_v0_8,
ActionPayload,
Catalog,
CatalogComponent,
} from '@ant-design/x-card';
XCard.Box;
XCard.Card;
🗂️ Component Architecture
XCard.Box
├── owns: catalogMap, surfaceCatalogMap
├── dispatches: commands → all XCard.Card children
├── aggregates: onAction events from all Cards
└── XCard.Card (id="surface-a")
│ ├── owns: component tree, data model, commandVersion
│ └── resolves: data bindings, triggers actions
└── XCard.Card (id="surface-b")
└── ...
XCard.Box Props
interface BoxProps {
commands?: (XAgentCommand_v0_9 | XAgentCommand_v0_8)[];
components?: Record<string, React.ComponentType<any>>;
onAction?: (payload: ActionPayload) => void;
children?: React.ReactNode;
}
XCard.Card Props
interface CardProps {
id: string;
}
ActionPayload
interface ActionPayload {
name: string;
surfaceId: string;
context: Record<string, any>;
}
🚀 Quick Start Decision Guide
🛠 Recommended Workflow
- Define your catalog — register a local catalog or use the A2UI Basic Catalog URL.
- Register custom components — pass them via
XCard.Box components prop.
- Create the React tree — wrap surfaces with
XCard.Box, add XCard.Card per surface.
- Feed commands — push
XAgentCommand_v0_9[] into commands prop (typically from streaming agent response).
- Handle actions — receive
ActionPayload in onAction, update commands in response.
Minimal Working Example
import React, { useState } from 'react';
import { XCard, registerCatalog } from '@ant-design/x-card';
import type { XAgentCommand_v0_9, ActionPayload, Catalog } from '@ant-design/x-card';
const myCatalog: Catalog = {
catalogId: 'local://my_catalog.json',
components: {
Text: {
type: 'object',
properties: { text: { type: 'string' }, variant: { type: 'string' } },
required: ['text'],
},
Button: {
type: 'object',
properties: { text: { type: 'string' }, action: {} },
required: ['text'],
},
},
};
registerCatalog(myCatalog);
const Text: React.FC<{ text: string; ?: }> = (
);
: .<{ : ; ?: ; ?: }> = ;
: _v0_9[] = [
{
: ,
: {
: ,
: ,
},
},
{
: ,
: {
: ,
: [
{ : , : , : [, ] },
{ : , : , : { : }, : },
{
: ,
: ,
: ,
: { : { : , : {} } },
},
],
},
},
{
: ,
: {
: ,
: ,
: ,
},
},
];
() {
[cmdQueue, setCmdQueue] = useState<_v0_9[]>(commands);
= () => {
.(, payload., payload.);
( [...prev ]);
};
(
);
}
🚨 Development Rules
- Always include
"version": "v0.9" on every command — omitting it causes protocol rejection.
- One and only one
id: "root" component per surface's component tree — this is the tree root.
- Flat adjacency list only — never nest component objects inside other component objects; always reference children by
id string.
- Separate structure from data —
updateComponents for layout, updateDataModel for content/state.
- Register catalog before mounting — call
registerCatalog() before the component tree renders.
- Pass
components map to XCard.Box, not to XCard.Card — Box distributes to all Cards.
- Never recreate the
components object inline — keep it stable with useMemo or module-level constant to avoid re-renders.
- Input components require
value: { path: "..." } for two-way binding — literal values do not update the data model.
- For streaming: append new commands to the array rather than replacing it — Card processes the diff incrementally.
action.event.context paths are write targets — they point to where user-entered data lives in the data model; do not resolve them as read sources.
- Path references in action context are automatically resolved — when an action is triggered, X-Card converts
{ path: "xxx" } in the action config to { value: "actual_value" } in the onAction payload. This works for both v0.9 (action.event.context = { key: { path } }) and v0.8 (action.context = [{ key, value: { path } }]) formats.
🤝 Skill Collaboration
| Scenario | Skill combination |
|---|
| AI chat with structured card responses | use-x-chat + x-components + x-card |
| Standalone agent form UI | x-card only |
| Streaming Markdown + card side-panel | x-markdown + x-card |
| HTTP streaming from agent into card | x-request → feed response as commands |
🔗 Reference Resources
- USAGE.md — Setup guide, streaming pattern, multi-surface examples
- COMMANDS.md — All four A2UI v0.9 command types, v0.8 vs v0.9 diff
- DATA_BINDING.md — JSON Pointer paths, dynamic types, two-way binding, template iteration
- ACTIONS.md — Action definitions, ActionPayload, form submission pattern
- CATALOG.md — Local catalog registration, remote URL loading, custom component schema
- API.md — Full TypeScript types for Box, Card, commands, catalog, actions
Official Documentation