Skip to content
Generative UIConfirmation cardMenu

Confirmation card

The safe pattern for actions an agent takes on the user's behalf: everything that will happen in plain rows, one explicit confirm, and a receipt afterwards. Confirm stays blocked while the summary streams or after the quote expires.

Every row that will happen, a held price with a countdown, and one explicit confirm that turns into a receipt.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/confirmation-card.json

Usage

tsx
import { ConfirmationCard } from "@/components/ui/chatcn/confirmation-card"

<ConfirmationCard
  title="Book this flight?"
  source="Requested by the travel agent"
  details={[{ label: "Flight", value: "UA 1532, SFO to JFK" }]}
  lineItems={[{ label: "Fare", amount: 412 }]}
  total={{ amount: 455.81 }}
  expiresAt={quote.expiresAt}
  confirmLabel="Book flight"
  onConfirm={async () => ({ reference: (await book(quote.id)).pnr })}
  onCancel={() => tell("The user declined.")}
/>

Examples

Every state

Streaming, confirmed, failed, expired and cancelled.

Safety

  • Nothing runs until the user presses confirm. The agent cannot confirm on their behalf.
  • Clicks in the first 600 ms after the card mounts are ignored (armDelayMs), so a card that streams in under the pointer cannot be confirmed by a click meant for something else.
  • Confirm is disabled while the summary is still streaming, so nobody agrees to rows they have not seen.
  • A held price counts down and expires; after that the card asks for a new quote instead of confirming a stale one.
  • source names who asked, so agent-initiated actions stay attributable.

Props

PropTypeNotes
title, description, sourceReactNode
details{ label, value, detail? }[]
lineItems, totalAmounts in currency
noteReactNodeTerms or refund policy
confirmLabel, cancelLabelstringSpecific verbs
onConfirm() => void | Promise<void | { reference? }>Reject to show the error
onCancel() => void
status, reference, errorControlled status for restored cards
expiresAtnumberEpoch ms
streaming, armDelayMs

Accessibility

  • The status (Needs your confirmation, Held for 9:42, Confirmed) is a live region. The countdown ticks once a second.
  • Errors use an icon and words and say whether anything was charged.

Motion

  • The status icon swaps with a 150 ms blur crossfade. The spinner sits on top of the confirm label, so the button keeps its width.