Hooks
useAdaptiveCard
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.
Client Only
Installation
pnpm dlx @docyrus/cli add @docyrus/hooks-use-adaptive-cardRequired Packages(1 package)
pnpm add reactOverview
useAdaptiveCard owns the runtime state for an AdaptiveCard instance:
- Inputs — a registry of every visible
Input.*element, keyed byid, with live values and per-input validation state. - Visibility overrides — the
Action.ToggleVisibilitymap layered on top of staticisVisibledefaults. - Show-card state — open/closed flags for every
Action.ShowCardbutton. - Action dispatch — typed event emission for
submit,execute,openUrl,toggleVisibility,showCard. - Submit payload assembly — walks the visible tree honoring
associatedInputs: 'auto' \| 'none', mergesaction.data, and emits a strongly typed event.
The <AdaptiveCard /> 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 <AdaptiveCard /> isn't enough)
'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 (
<div>
<button onClick={() => adaptive.controls.validateAll()}>Validate</button>
<AdaptiveCardView cardProps={adaptive.cardProps} />
</div>
);
}Reading current input state
const adaptive = useAdaptiveCard(payload);
// Live, controlled
console.log(adaptive.state.inputs.email);
console.log(adaptive.state.validations.email);Programmatic submit
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
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<void> | — | Receives every dispatched action. |
hostConfig | AdaptiveCardHostConfigOverride | — | Deep-merged onto defaultHostConfig. |
customElements | Record<string, ElementRenderer> | {} | 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<string, AdaptiveCardInputValue> | Live input values, keyed by id. |
state.validations | Record<string, { isValid; errorMessage? }> | Per-input validation, recomputed on every input change. |
state.visibilityOverrides | Record<string, boolean> | Action.ToggleVisibility overrides keyed by element id. |
state.showCardOpen | Record<string, boolean> | 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 <AdaptiveCardView />. |
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 everyInput.*whoseisVisibleevaluation is true under the currentvisibilityOverridesmap. Hidden inputs are dropped.'none'— skip inputs entirely; emit only the literalaction.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<string> | boolean | number | null. |