# Adaptive Card Designer
URL: /docs/web/components/adaptive-card-designer
Visual drag-and-drop designer for Microsoft Adaptive Cards 1.5 / 1.6 payloads. Three-pane editor (toolbox · canvas · structure / properties) with paired JSON editors, live preview, undo / redo, and theme / width controls — backed by the in-repo Adaptive Card renderer.
**Demo:**
```tsx
'use client';
import { useState } from 'react';
import { AdaptiveCardDesigner } from '@docyrus/ui/components/adaptive-card-designer';
import { type AdaptiveCardPayload } from '@docyrus/ui/components/adaptive-card';
import { Button } from '@docyrus/ui/primitives/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle
} from '@docyrus/ui/primitives/ui/dialog';
import { LayoutTemplate, Plus } from 'lucide-react';
const STARTER: AdaptiveCardPayload = {
type: 'AdaptiveCard',
version: '1.5',
body: [
{
type: 'TextBlock',
text: '**Welcome to the Adaptive Card Designer**',
size: 'large',
weight: 'bolder'
},
{
type: 'TextBlock',
text: 'Pick elements from the toolbox on the left, or edit the JSON below. The canvas updates live.',
wrap: true,
isSubtle: true
},
{
type: 'FactSet',
facts: [{ title: 'Status', value: 'Draft' }, { title: 'Owner', value: '${user.name}' }]
}
],
actions: [{ type: 'Action.OpenUrl', title: 'Adaptive Cards spec', url: 'https://adaptivecards.io/' }]
};
const STARTER_DATA = {
user: { name: 'Alice', role: 'Admin' }
};
export function AdaptiveCardDesignerDemo() {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState<'sample' | 'blank'>('sample');
const openDesigner = (next: 'sample' | 'blank') => {
setMode(next);
setOpen(true);
};
return (
);
}
```
## Installation
```bash
pnpm dlx @docyrus/cli add @docyrus/ui-adaptive-card-designer
```
**Dependencies:**
- [@dnd-kit/core](https://www.npmjs.com/package/@dnd-kit/core)
- [@monaco-editor/react](https://www.npmjs.com/package/@monaco-editor/react)
- [lucide-react](https://www.npmjs.com/package/lucide-react)
The designer renders its canvas with the [`AdaptiveCard`](/docs/web/components/adaptive-card) renderer, which is installed automatically as a registry dependency.
## Overview
`AdaptiveCardDesigner` is a full visual authoring surface for [Adaptive Cards](https://adaptivecards.io/) payloads. Where [`AdaptiveCard`](/docs/web/components/adaptive-card) only *renders* a payload, the designer lets users *build* one — dragging elements from a toolbox, reordering and reparenting them in a structure tree, editing element properties in a side panel, and watching the result render live. The raw payload and the templating sample-data are both editable as JSON at the bottom, and the two views stay in sync.
Typical use cases:
- **Internal card authoring** — a no-code surface for ops / support teams to compose Teams-style cards and agent UI without hand-writing JSON.
- **Template tooling** — author the payload that an LLM agent or automation later fills with `${...}` bindings, previewed against sample data.
- **Embedded editors** — drop the designer into an automation-node config or a CMS, read the emitted payload back via `onChange`.
The designer works on its own normalized node tree internally (stable per-node ids, uniform child *slots*) and serializes back to a standard `AdaptiveCardPayload` on every edit.
## Layout
The designer is a three-pane layout with a toolbar above and paired JSON editors below:
| Region | Purpose |
|--------|---------|
| **Toolbar** | New / undo / redo / copy, theme (light / dark / auto), canvas width (standard / wide / full), preview toggle. Buttons are individually hideable via `hideToolbarButtons`. |
| **Toolbox** (left) | Draggable element / input / action types grouped into `elements`, `inputs`, `actions`, `advanced`. Append your own via `extraToolboxItems`. |
| **Canvas** (center) | Live render of the card via ``. Click an element to select it; drop toolbox items directly onto it. |
| **Structure** (right, top) | Collapsible tree of the card. Reorder / reparent by drag, select to edit. |
| **Properties** (right, bottom) | Type-aware property editors for the selected node. |
| **Payload editor** (bottom) | The live `AdaptiveCardPayload` JSON (Monaco). |
| **Sample-data editor** (bottom) | The templating data JSON used to resolve `${...}` bindings in preview. |
## Usage
### Uncontrolled
```tsx
import { AdaptiveCardDesigner } from '@docyrus/ui/components/adaptive-card-designer';
import { type AdaptiveCardPayload } from '@docyrus/ui/components/adaptive-card';
const starter: AdaptiveCardPayload = {
type: 'AdaptiveCard',
version: '1.5',
body: [{ type: 'TextBlock', text: 'Hello, **Adaptive Cards**!', weight: 'bolder' }]
};
console.log(payload, sampleData)} />
```
### Controlled
Pass `payload` / `sampleData` to drive the designer from outside. `onChange` fires after every edit:
```tsx
const [payload, setPayload] = useState(starter);
const [data, setData] = useState({});
{ setPayload(next.payload); setData(next.sampleData); }} />
```
### Read-only inspection
`readOnly` disables every mutation — toolbox drag, drop zones, property edits, JSON edits, undo / redo / new, and keyboard shortcuts. Theme / width / preview / copy stay active, and selection remains enabled so users can click through the tree and inspect properties:
```tsx
```
### Custom toolbox items
Append your own draggable types alongside the built-ins. Each item supplies a `factory` that returns a fresh node on every drop:
```tsx
import { Sparkles } from 'lucide-react';
import { type ToolboxItem } from '@docyrus/ui/components/adaptive-card-designer';
const extras: ToolboxItem[] = [
{
id: 'Docyrus.UserChip',
type: 'Docyrus.UserChip',
label: 'User Chip',
icon: Sparkles,
group: 'advanced',
keywords: ['user', 'chip', 'avatar'],
factory: () => ({
__designerId: crypto.randomUUID(),
type: 'Docyrus.UserChip',
props: { userId: '' },
slots: {}
})
}
];
}} />
```
Pair `extraToolboxItems` (authoring) with `customElements` (rendering) so the canvas knows how to draw the type you just added. `customElements` and `hostConfig` are forwarded straight to the inner ``.
### Trimming the toolbar
```tsx
```
## API Reference
### `AdaptiveCardDesignerProps`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `payload` | `AdaptiveCardPayload` | — | Current card payload (controlled). |
| `defaultPayload` | `AdaptiveCardPayload` | — | Initial payload when uncontrolled. |
| `sampleData` | `unknown` | — | Templating data resolved into the preview canvas (controlled). |
| `defaultSampleData` | `unknown` | — | Initial sample data when uncontrolled. |
| `onChange` | `(next: { payload: AdaptiveCardPayload; sampleData: unknown }) => void` | — | Fires after every payload or sample-data edit. |
| `hostConfig` | `AdaptiveCardHostConfigOverride` | — | Forwarded to the inner ``. |
| `customElements` | `Record` | `{}` | Forwarded to the inner ``. Pair with `extraToolboxItems` to author custom types. |
| `defaultPreview` | `boolean` | `false` | Start in preview (render-only) mode rather than edit mode. |
| `defaultTheme` | `DesignerTheme` | `'light'` | Initial canvas theme — `'light' \| 'dark' \| 'auto'`. |
| `defaultWidth` | `DesignerWidth` | `'standard'` | Initial canvas width — `'standard' \| 'wide' \| 'full'`. |
| `hideToolbarButtons` | `ToolbarButtonKey[]` | — | Hide specific toolbar buttons — `'new' \| 'theme' \| 'width' \| 'undo' \| 'redo' \| 'copy' \| 'preview'`. |
| `extraToolboxItems` | `ToolboxItem[]` | — | Append additional toolbox items alongside the built-ins. |
| `height` | `string` | `'70vh'` | Designer chrome height. |
| `className` | `string` | — | Forwarded to the root element. |
| `readOnly` | `boolean` | `false` | Disable all mutations. Theme / width / preview / copy and selection stay active. |
| `aiAssistantOpen` | `boolean` | — | Controlled open state for the AI Assistant drawer. When provided the designer stops managing the open state internally — pair with `onAiAssistantOpenChange`. |
| `onAiAssistantOpenChange` | `(open: boolean) => void` | — | Fired when the AI Assistant toolbar button toggles the drawer. |
| `renderAiAssistant` | `(ctx: IAdaptiveCardAiAssistantRenderContext) => ReactNode` | — | Mounts a custom AI Assistant drawer body on the left of the designer. When set, the toolbar shows a Bot toggle that opens/closes the drawer; this render fn supplies the body (typically a `` or `` wrapper). Without it the AI button is hidden. |
| `aiAssistantWidth` | `number` | `380` | Width in pixels the drawer animates to when open. |
## EditorAgent integration
Pair `useApplyAdaptiveCard` with `renderAiAssistant` to let an LLM author Adaptive Card payloads. The hook owns the designer's controlled `payload` + `sampleData` and exposes an `applyAdaptiveCard` tool that the agent calls to commit a new payload. The tool rejects primitives / arrays / null and requires `type: "AdaptiveCard"` at the root so a malformed call cannot crash the designer.
```tsx
import { EditorAgent } from '@docyrus/ui/components/editor-agent';
import { AdaptiveCardDesigner, useApplyAdaptiveCard } from '@docyrus/ui/components/adaptive-card-designer';
export function AdaptiveCardPlayground({ client, user, agentId }) {
const adaptiveCard = useApplyAdaptiveCard();
const [aiOpen, setAiOpen] = useState(false);
return (
{
adaptiveCard.setPayload(next.payload);
adaptiveCard.setSampleData(next.sampleData);
}}
aiAssistantOpen={aiOpen}
onAiAssistantOpenChange={setAiOpen}
renderAiAssistant={({ open, onClose }) => (
)}
/>
);
}
```
The matching backend agent must register a tool named `applyAdaptiveCard` whose input schema mirrors `{ payload: object (required), sampleData?: any, explanation?: string }`.
## Composition
The default `AdaptiveCardDesigner` is a single component that composes a provider + the panes. For custom layouts you can assemble the pieces yourself:
| Export | Purpose |
|--------|---------|
| `DesignerProvider` | Holds reducer state + history. Wrap your own panel composition in it. |
| `useDesignerContext()` | Read `state` + `dispatch` inside a custom panel. |
| `useApplyAdaptiveCard()` | Owns the controlled `payload` + `sampleData` state and ships the `applyAdaptiveCard` client-side tool for ``. See **EditorAgent integration** above. |
## Helpers
The component also exports its tree model utilities for advanced integrations (custom serializers, programmatic edits, validation):
| Export | Purpose |
|--------|---------|
| `cardToTree(payload)` / `treeToCard(node)` | Convert between an `AdaptiveCardPayload` and the designer's `DesignerNode` tree. |
| `normalizeForRoundTrip(payload)` | Normalize a payload so tree → card → tree is lossless. |
| `slotsFor(type)` / `SLOT_MAP` | The slot names (`items`, `columns`, `actions`, …) a given element type accepts. |
| `isLeafType(type)` | Whether a type accepts no children. |
| `createDefaultNode(type)` / `defaultChildType(type)` | Factory helpers for new nodes. |
| `createDesignerId()` | Generate a stable internal node id. |
| `findNode`, `findLocation`, `insertNode`, `removeNode`, `moveNode`, `updateNode`, `isAncestor`, `collectIds`, `countNodes` | Tree traversal + mutation utilities. |
## Type Exports
| Type | Description |
|------|-------------|
| `AdaptiveCardDesignerProps` | Component props. |
| `DesignerProviderProps` | Props for `DesignerProvider`. |
| `DesignerNode` | A node in the designer's normalized tree — `{ __designerId, type, props, slots }`. |
| `DesignerState` / `DesignerAction` | Reducer state + action union. |
| `DesignerTheme` | `'light' \| 'dark' \| 'auto'`. |
| `DesignerWidth` | `'standard' \| 'wide' \| 'full'`. |
| `DesignerFocus` | Which editor was last focused — `'canvas' \| 'payload' \| 'data'`. |
| `DesignerDiagnostic` | A validation diagnostic — `{ level, message, nodeId? }`. |
| `HistorySnapshot` | An undo / redo history entry. |
| `ToolboxItem` / `ToolboxGroup` | A toolbox entry and its group key. |
| `ToolbarButtonKey` | Keys accepted by `hideToolbarButtons`. |
| `DesignerRootType` | The `'__root'` discriminant for the card root node. |
| `IAdaptiveCardAiAssistantRenderContext` | Argument passed to `renderAiAssistant` — `{ open, width, onClose, payload, sampleData }`. |
| `IUseApplyAdaptiveCardResult` | Return shape of `useApplyAdaptiveCard` — `{ payload, setPayload, sampleData, setSampleData, tools }`. |
## Related
- [`AdaptiveCard`](/docs/web/components/adaptive-card) — the renderer the designer canvas is built on.
- [`useAdaptiveCard`](/docs/web/hooks/use-adaptive-card) — the hook behind the renderer.
---
# Adaptive Card
URL: /docs/web/components/adaptive-card
Renderer for Microsoft's Adaptive Cards 1.5 schema. Surfaces LLM-generated and Microsoft Teams payloads as native Docyrus UI with stateful inputs, validation, nested ShowCards, and action dispatch.
**Demo:**
```tsx
'use client';
import { useMemo, useState } from 'react';
import {
AdaptiveCard,
type AdaptiveCardActionEvent,
type AdaptiveCardPayload
} from '@docyrus/ui/components/adaptive-card';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@docyrus/ui/primitives/ui/select';
import { cn } from '@docyrus/ui/primitives/lib/utils';
import {
DEMO_AGENT_MESSAGE,
DEMO_FLIGHT_CARD,
DEMO_FORM_CARD
} from '@/data/adaptive-card-data';
const DEMOS = {
flight: { label: 'Flight status', payload: DEMO_FLIGHT_CARD },
form: { label: 'Form card', payload: DEMO_FORM_CARD },
agent: { label: 'Agent message', payload: DEMO_AGENT_MESSAGE }
} as const satisfies Record;
export function AdaptiveCardDemo() {
const [selected, setSelected] = useState('flight');
const [open, setOpen] = useState(false);
const [lastEvent, setLastEvent] = useState
))}
{lastEvent ? (
{JSON.stringify(lastEvent, (_k, v) => v instanceof Object && 'card' in v ? { ...v, card: '<…>' } : v, 2)}