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/confirmerConfiguration
confirm() accepts ConfirmerOptions:
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Dialog title (required). |
description | string | — | Optional body HTML (rendered with dangerouslySetInnerHTML). |
variant | AlertDialogAction variant | "default" | Visual style of the confirm button. |
cancelButtonTitle | string | "Cancel" | Label for the cancel button. |
confirmButtonTitle | string | "OK" | Label for the confirm button. |
children | React.ReactNode | — | Extra 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.ReactNode | — | Custom 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)
}
},
})