# useAdaptiveCard
URL: /docs/web/hooks/use-adaptive-card
Headless state, validation, visibility, and action dispatch for the Adaptive Cards 1.5 renderer. Drives the `AdaptiveCard` component and is exported so consumers can wire custom toolbars or server-side validation.
## Installation
```bash
pnpm dlx @docyrus/cli add @docyrus/hooks-use-adaptive-card
```
**Dependencies:**
- [react](https://react.dev)
## Overview
`useAdaptiveCard` owns the runtime state for an [`AdaptiveCard`](/docs/web/components/adaptive-card) instance:
- **Inputs** — a registry of every visible `Input.*` element, keyed by `id`, with live values and per-input validation state.
- **Visibility overrides** — the `Action.ToggleVisibility` map layered on top of static `isVisible` defaults.
- **Show-card state** — open/closed flags for every `Action.ShowCard` button.
- **Action dispatch** — typed event emission for `submit`, `execute`, `openUrl`, `toggleVisibility`, `showCard`.
- **Submit payload assembly** — walks the visible tree honoring `associatedInputs: 'auto' \| 'none'`, merges `action.data`, and emits a strongly typed event.
The `` component composes this hook internally. You only need to call it directly when:
- You want to trigger actions programmatically (e.g. a custom toolbar that says "Submit & close").
- You need to read input state from outside the card (e.g. to disable a parent's submit button).
- You want to wire server-side validation that returns errors per `inputId`.
## Usage
### Default integration (when `` isn't enough)
```tsx
'use client';
import {
AdaptiveCardView,
useAdaptiveCard,
type AdaptiveCardActionEvent,
type AdaptiveCardPayload
} from '@docyrus/ui/components/adaptive-card';
export function CardWithToolbar({ payload }: { payload: AdaptiveCardPayload }) {
function handleAction(event: AdaptiveCardActionEvent) {
console.log(event);
}
const adaptive = useAdaptiveCard(payload, { onAction: handleAction });
return (
);
}
```
### Reading current input state
```tsx
const adaptive = useAdaptiveCard(payload);
// Live, controlled
console.log(adaptive.state.inputs.email);
console.log(adaptive.state.validations.email);
```
### Programmatic submit
```tsx
const submitAction = payload.actions?.find(a => a.type === 'Action.Submit');
if (submitAction && submitAction.type === 'Action.Submit') {
adaptive.controls.submit(submitAction);
}
```
### Resetting after a successful save
```tsx
async function handleAction(event: AdaptiveCardActionEvent) {
if (event.type !== 'submit') return;
await fetch('/api/forms', { method: 'POST', body: JSON.stringify(event.data) });
adaptive.controls.reset(); // clears inputs, visibility overrides, show-card state
}
```
## API Reference
### `UseAdaptiveCardOptions`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `onAction` | `(event: AdaptiveCardActionEvent) => void \| Promise` | — | Receives every dispatched action. |
| `hostConfig` | `AdaptiveCardHostConfigOverride` | — | Deep-merged onto `defaultHostConfig`. |
| `customElements` | `Record` | `{}` | Renderers for unknown `type` strings. |
### `UseAdaptiveCardReturn`
| Field | Type | Description |
|-------|------|-------------|
| `card` | `AdaptiveCardPayload` | The original payload (post-`parseAdaptiveCard` normalization). |
| `hostConfig` | `AdaptiveCardHostConfig` | The resolved host config (default deep-merged with override). |
| `state.inputs` | `Record` | Live input values, keyed by `id`. |
| `state.validations` | `Record` | Per-input validation, recomputed on every input change. |
| `state.visibilityOverrides` | `Record` | `Action.ToggleVisibility` overrides keyed by element `id`. |
| `state.showCardOpen` | `Record` | Open flags for `Action.ShowCard`, keyed by action `id`/`title`. |
| `state.hasSubmittedOnce` | `boolean` | Becomes `true` after the first failed submit so per-input error messages render. |
| `controls.setInput(id, value)` | `(id, value) => void` | Imperatively set an input. |
| `controls.submit(action)` | `(action: AdaptiveCardActionSubmit) => void` | Validate + dispatch `'submit'`. |
| `controls.execute(action)` | `(action: AdaptiveCardActionExecute) => void` | Validate + dispatch `'execute'` with `verb`. |
| `controls.openUrl(action)` | `(action: AdaptiveCardActionOpenUrl) => void` | Dispatch `'openUrl'`. |
| `controls.toggleVisibility(action)` | `(action: AdaptiveCardActionToggleVisibility) => void` | Apply visibility overrides + dispatch `'toggleVisibility'`. |
| `controls.toggleShowCard(action)` | `(action: AdaptiveCardActionShowCard) => void` | Flip a show-card and dispatch `'showCard'`. |
| `controls.validateAll()` | `() => { ok; firstInvalidId?; errors }` | Run validation immediately. Does not dispatch. |
| `controls.reset()` | `() => void` | Clear inputs / overrides / show-card state. |
| `cardProps` | `AdaptiveCardContextValue` | The projection consumed by ``. |
## Validation timing
- **First submit** runs validation silently. If any input fails, the hook sets `state.hasSubmittedOnce = true`, focuses the first invalid input, and does **not** dispatch.
- After the first failed submit, validations re-render live so error messages clear as the user fixes them.
- This matches the reference renderer's behavior — users don't see errors before they've tried to submit.
## `associatedInputs` contract
`Action.Submit` and `Action.Execute` walk the **currently visible** card tree to build the submit payload:
- `'auto'` (default) — collect every `Input.*` whose `isVisible` evaluation is true under the current `visibilityOverrides` map. Hidden inputs are dropped.
- `'none'` — skip inputs entirely; emit only the literal `action.data`.
For `Action.ShowCard`-nested submits, the nested card's inputs merge into the parent's, mirroring the spec.
## Type Exports
| Type | Description |
|------|-------------|
| `UseAdaptiveCardOptions` | Hook options. |
| `UseAdaptiveCardReturn` | Full hook return. |
| `AdaptiveCardInputValue` | `string \| Array \| boolean \| number \| null`. |