Layers

Confirmer

Overview

Imperative confirmation dialog with async action support, loading state, and inline error display. Call confirm() to open the dialog and optionally run an async action.

Install

Configure your registry in components.json:

{
  "registries": {
    "@uxio": "https://ui.uxio.dev/r/styles/{style}/{name}.json"
  }
}

Then run:

npx shadcn@latest add @uxio/confirmer

Configuration

confirm() accepts ConfirmerOptions:

PropTypeDefaultDescription
titlestringDialog title (required).
descriptionstringOptional body HTML (rendered with dangerouslySetInnerHTML).
variantAlertDialogAction variant"default"Visual style of the confirm button.
cancelButtonTitlestring"Cancel"Label for the cancel button.
confirmButtonTitlestring"OK"Label for the confirm button.
childrenReact.ReactNodeExtra content between the description block and the footer.
displayError"none" | "above-content" | "below-content""below-content"Where to show an error when action rejects. "none" hides inline error UI (the error is still captured).
renderError(error: Error) => React.ReactNodeCustom error UI. If omitted, a default destructive-styled message with an icon is used.
action() => void | Promise<void>Runs when the user confirms. Shows a loading spinner on the confirm button; on failure, the promise rejection is shown per displayError.

Usage

Place <Confirmer /> once in your root layout (e.g. Next.js app/layout.tsx, TanStack Start root, or the top-level route that wraps the whole app). It must stay mounted so confirm() can open the dialog from anywhere.

// app/layout.tsx (or equivalent root layout)
import { Confirmer } from "@/components/ui/confirmer"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Confirmer />
      </body>
    </html>
  )
}

Then use confirm() from any component.

Return boolean

Await confirm() to get a boolean. Use when you need to branch based on user choice:

const confirmed = await confirm({
  title: "Close document",
  description: "Your changes will not be saved.",
  variant: "default",
  confirmButtonTitle: "OK",
})

if (confirmed) {
  // do something
}

Action with loading and error

Pass an action to run async work. The dialog shows a loading spinner and can display errors inline:

void confirm({
  title: "Delete Post",
  description: "This action cannot be reversed. Are you sure you want to proceed?",
  variant: "destructive",
  confirmButtonTitle: "Delete post",
  displayError: "below-content",
  action: async () => {
    const result = await deleteCurrentPost()
    if (result.data) {
      router.replace("/")
    } else {
      throw new Error(result.error)
    }
  },
})

Examples

Return boolean

Action with loading