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-card
Required Packages(1 package)
pnpm add react

Overview

useAdaptiveCard owns the runtime state for an AdaptiveCard 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 <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

OptionTypeDefaultDescription
onAction(event: AdaptiveCardActionEvent) => void | Promise<void>—Receives every dispatched action.
hostConfigAdaptiveCardHostConfigOverride—Deep-merged onto defaultHostConfig.
customElementsRecord<string, ElementRenderer>{}Renderers for unknown type strings.

UseAdaptiveCardReturn

FieldTypeDescription
cardAdaptiveCardPayloadThe original payload (post-parseAdaptiveCard normalization).
hostConfigAdaptiveCardHostConfigThe resolved host config (default deep-merged with override).
state.inputsRecord<string, AdaptiveCardInputValue>Live input values, keyed by id.
state.validationsRecord<string, { isValid; errorMessage? }>Per-input validation, recomputed on every input change.
state.visibilityOverridesRecord<string, boolean>Action.ToggleVisibility overrides keyed by element id.
state.showCardOpenRecord<string, boolean>Open flags for Action.ShowCard, keyed by action id/title.
state.hasSubmittedOncebooleanBecomes true after the first failed submit so per-input error messages render.
controls.setInput(id, value)(id, value) => voidImperatively set an input.
controls.submit(action)(action: AdaptiveCardActionSubmit) => voidValidate + dispatch 'submit'.
controls.execute(action)(action: AdaptiveCardActionExecute) => voidValidate + dispatch 'execute' with verb.
controls.openUrl(action)(action: AdaptiveCardActionOpenUrl) => voidDispatch 'openUrl'.
controls.toggleVisibility(action)(action: AdaptiveCardActionToggleVisibility) => voidApply visibility overrides + dispatch 'toggleVisibility'.
controls.toggleShowCard(action)(action: AdaptiveCardActionShowCard) => voidFlip a show-card and dispatch 'showCard'.
controls.validateAll()() => { ok; firstInvalidId?; errors }Run validation immediately. Does not dispatch.
controls.reset()() => voidClear inputs / overrides / show-card state.
cardPropsAdaptiveCardContextValueThe 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 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

TypeDescription
UseAdaptiveCardOptionsHook options.
UseAdaptiveCardReturnFull hook return.
AdaptiveCardInputValuestring | Array<string> | boolean | number | null.

On this page