Components / Actions

ConfirmMorph

A destructive button that morphs into an inline confirmation, a spinner, and a result with undo.

Install

npx shadcn@latest add https://irisui.dev/r/confirm-morph.json
import { ConfirmMorph } from "@/components/iris/confirm-morph/confirm-morph";
Registry item confirm-morphBrings in foundationRegistry JSON

Preview

ConfirmMorph

A destructive or important button that asks in place: it morphs into an inline question with Cancel and Confirm, then a spinner, then a result with Undo.

When to use

  • Deleting a project, revoking access, discarding a draft — where a modal dialog would be heavy but a single tap is too easy.
  • When the action can be undone for a moment (pass onUndo), or should run after a short cancellable countdown (confirmMode="countdown").
  • When people must read consequences before confirming, use a dialog. For a hold gesture with no question, use HoldToConfirm.

Usage

import { ConfirmMorph } from "@/components/iris/confirm-morph/confirm-morph";

<ConfirmMorph
  label="Delete project"
  icon={<TrashIcon size={16} />}
  prompt="Delete project blog?"
  onConfirm={() => api.deleteProject(id)}
  onUndo={() => api.restoreProject(id)}
/>;

Props

Prop Type Default Description
label ReactNode — Resting label (required).
icon ReactNode — Icon before the resting label.
prompt ReactNode label + "?" The question while confirming.
confirmLabel / cancelLabel string "Delete" / "Cancel" Buttons in the question.
pendingLabel / doneLabel / errorLabel / retryLabel string "Deleting" / "Deleted" / "Couldn’t finish" / "Retry" Labels for the later faces.
undoLabel / undoingLabel string "Undo" / "Restoring" Undo button and its pending text.
tone "danger" | "neutral" "danger" Confirm button colour: danger, or the accent for reversible actions.
confirmMode "press" | "hold" | "countdown" "press" How the question is answered.
holdDuration number 1000 Ms to hold in hold mode.
countdown / countdownLabel number / string 3 / pending label + "in" Seconds and text in countdown mode.
onConfirm () => void | Promise<unknown> — Runs on confirm. A promise shows the pending face; a rejection shows the error face with Retry.
onUndo () => void | Promise<unknown> — Adds Undo to the result.
onCancel () => void — Called when the question is cancelled.
state / defaultState / onStateChange "idle" | "confirming" | "pending" | "done" | "error" "idle" Controlled or uncontrolled state.
confirmTimeout / resultTimeout number 6000 / 5000 Ms before an unanswered question or a result returns to rest; hovering pauses it; 0 turns it off.
cancelOnOutsidePress boolean true A press outside cancels an open question.
disabled, className, ref — — ref receives the root element.

Notes

  • Escape, an outside press or the timeout return it to rest. Focus follows the control between faces only if it was already inside, and the root takes focus while pending.
  • The question is labelled for assistive tech, and a polite live region (<output>) announces pending, done and error faces.
  • Width springs between faces while faces crossfade at their natural width, so text never squashes. Reduced motion uses instant fades.