Hooks

useAdaptiveCard

Headless state, validation, visibility and action dispatch for the native Adaptive Cards renderer. Drives the AdaptiveCard component and is exported so apps can wire custom toolbars or server-side validation.

iOSAndroidExpo Go

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-adaptive-card
Required Packages(1 package)
pnpm add react

The hook ships with the AdaptiveCard component (it imports the card's types and lib/ helpers). It is also re-exported from @/components/docyrus-native/adaptive-card, and the old components/adaptive-card/use-adaptive-card path still resolves.

Overview

useAdaptiveCard owns the runtime state of one card. The public API matches web @docyrus/ui 1:1.

  • Inputs. Every visible Input.* element is registered by id, with its live value and validation state.
  • Visibility overrides. Action.ToggleVisibility results are layered on top of the static isVisible defaults.
  • Show-card state. Each Action.ShowCard button has an open or closed flag.
  • Action dispatch. submit, execute, openUrl, toggleVisibility, showCard and resetInputs are emitted as typed events.
  • Submit payload assembly. The hook walks the visible tree, honours associatedInputs: 'auto' | 'none' and merges action.data.
  • Focus on the first invalid input. When a submit or execute fails validation, the hook focuses the first invalid input. The native equivalent of the web [aria-invalid] DOM lookup is a focus registry: Input.Text and Input.Number register their TextInput ref, so the keyboard opens on that field and the enclosing scroll view brings it into view.

<AdaptiveCard /> uses this hook internally. Call it yourself only when you need one of these:

  • Trigger actions programmatically, for example from a native header button.
  • Read input state from outside the card.
  • Wire server-side validation that returns errors per input id.

Usage

Custom integration

import { Button, View } from 'react-native';

import {
  AdaptiveCardView,
  useAdaptiveCard,
  type AdaptiveCardActionEvent,
  type AdaptiveCardPayload
} from '@/components/docyrus-native/adaptive-card';

export function CardWithToolbar({ payload }: { payload: AdaptiveCardPayload }) {
  function handleAction(event: AdaptiveCardActionEvent) {
    console.log(event);
  }

  const adaptive = useAdaptiveCard(payload, { onAction: handleAction });

  return (
    <View className="gap-3">
      <Button title="Validate" onPress={() => adaptive.controls.validateAll()} />
      <AdaptiveCardView cardProps={adaptive.cardProps} />
    </View>
  );
}

Reading the current input state

const adaptive = useAdaptiveCard(payload);

console.log(adaptive.state.inputs.email);
console.log(adaptive.state.validations.email);

Submitting programmatically

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 api.post('/forms', event.data);
  adaptive.controls.reset(); // clears inputs, visibility overrides and show-card state
}

API Reference

function useAdaptiveCard(card: AdaptiveCardPayload, options?: UseAdaptiveCardOptions): UseAdaptiveCardReturn;

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.
onChoiceQuery(request: AdaptiveCardChoiceQueryRequest) => Promise<Array<AdaptiveCardChoice>>—Async resolver for a filtered Input.ChoiceSet whose choices.data is a Data.Query. The input is debounced, and the returned choices are merged with the static choices. A throwing resolver yields [].

AdaptiveCardChoiceQueryRequest

FieldTypeDescription
datasetstringThe Data.Query dataset name.
searchstringThe current filter text.
inputIdstringThe Input.ChoiceSet id.

UseAdaptiveCardReturn

FieldTypeDescription
cardAdaptiveCardPayloadThe payload passed in.
hostConfigAdaptiveCardHostConfigThe resolved host config: the default deep-merged with your override.
state.inputsRecord<string, AdaptiveCardInputValue>Live input values, keyed by id.
state.validationsRecord<string, ValidationResult>Validation for each input ({ isValid, errorMessage? }), recomputed on every change.
state.visibilityOverridesRecord<string, boolean>Action.ToggleVisibility overrides, keyed by element id.
state.showCardOpenRecord<string, boolean>Open flags for Action.ShowCard, keyed by the action's id or title.
state.hasSubmittedOncebooleanBecomes true after the first failed submit, which is when per-input errors start to show.
controls.setInput(id, value)(id, value) => voidSets an input imperatively.
controls.submit(action)(action: AdaptiveCardActionSubmit) => voidValidates, then dispatches 'submit'. On failure, focuses the first invalid input instead.
controls.execute(action)(action: AdaptiveCardActionExecute) => voidValidates, then dispatches 'execute' with verb. On failure, focuses the first invalid input instead.
controls.openUrl(action)(action: AdaptiveCardActionOpenUrl) => voidDispatches 'openUrl'.
controls.toggleVisibility(action)(action: AdaptiveCardActionToggleVisibility) => voidApplies the visibility overrides, then dispatches 'toggleVisibility'.
controls.toggleShowCard(action)(action: AdaptiveCardActionShowCard) => voidFlips a show-card, then dispatches 'showCard'.
controls.resetInputs(action)(action: AdaptiveCardActionResetInputs) => voidClears the target inputs (all of them when targetInputIds is empty), then dispatches 'resetInputs'.
controls.validateAll()() => { ok; firstInvalidId?; errors }Runs validation immediately. It does not dispatch or change focus.
controls.reset()() => voidClears inputs, visibility overrides and show-card state.
cardPropsAdaptiveCardContextValueThe value <AdaptiveCardView /> consumes. On native it also carries registerInputFocus for the focus registry.

When validation runs

  • The first submit validates silently. If any input fails, the hook sets state.hasSubmittedOnce = true, focuses the first invalid focusable input, and does not dispatch.
  • After that failed submit, validation re-renders live, so each error message clears once the user fixes the field.
  • Date, time, toggle, choice-set and rating inputs have no text caret to focus. They show their error state but are not focused.

The associatedInputs contract

Action.Submit and Action.Execute build the submit payload from the currently visible card tree:

  • 'auto' (default): collects every Input.* that is visible under the current visibilityOverrides. Hidden inputs are left out.
  • 'none': skips inputs and emits only the literal action.data.

Type Exports

TypeDescription
UseAdaptiveCardOptionsThe hook options.
UseAdaptiveCardReturnThe full hook return value.
AdaptiveCardChoiceQueryRequestThe request passed to onChoiceQuery.
AdaptiveCardInputValuestring | Array<string> | boolean | number | null.

On this page