Components / Feedback
Confirm Dialog
The gate in front of something irreversible: a question, its consequence, and two unequal answers.
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
ConfirmDialog,
ConfirmDialogAction,
ConfirmDialogBody,
ConfirmDialogCancel,
ConfirmDialogChallenge,
ConfirmDialogContent,
ConfirmDialogDescription,
ConfirmDialogFooter,
ConfirmDialogTitle,
ConfirmDialogTrigger,
} from "@/components/ui/hiip-ui/confirm-dialog";
function ConfirmDialogDemo() {
const [open, setOpen] = useState(false);
const [gatedOpen, setGatedOpen] = useState(false);
const [pending, setPending] = useState(false);
// Stands in for the request. The dialog stays up while it runs — that is the
// whole reason the action button is not a `Close`.
function close() {
setPending(true);
setTimeout(() => {
setPending(false);
setOpen(false);
}, 900);
}
return (
<div className="flex flex-wrap items-center gap-3">
<ConfirmDialog open={open} onOpenChange={setOpen}>
<ConfirmDialogTrigger render={<Button variant="destructive" />}>
Close terminal
</ConfirmDialogTrigger>
<ConfirmDialogContent>
<ConfirmDialogBody>
<ConfirmDialogTitle>Close this terminal?</ConfirmDialogTitle>
<ConfirmDialogDescription>
The shell and anything running in it are killed. This can’t
be undone — a closed pty has no scrollback to come back to.
</ConfirmDialogDescription>
</ConfirmDialogBody>
<ConfirmDialogFooter>
<ConfirmDialogCancel disabled={pending} />
<ConfirmDialogAction pending={pending} onClick={close}>
Close
</ConfirmDialogAction>
</ConfirmDialogFooter>
</ConfirmDialogContent>
</ConfirmDialog>
{/* The same item with a typed gate: the action stays disabled until the
workspace name is typed out, and Enter in the field confirms. */}
<ConfirmDialog
open={gatedOpen}
onOpenChange={setGatedOpen}
challenge="mosclub-mencare"
>
<ConfirmDialogTrigger render={<Button variant="outline" />}>
Delete workspace
</ConfirmDialogTrigger>
<ConfirmDialogContent>
<ConfirmDialogBody>
<ConfirmDialogTitle>Delete this workspace?</ConfirmDialogTitle>
<ConfirmDialogDescription>
Every campaign, creator list and export in it goes with it. Four
people share this workspace.
</ConfirmDialogDescription>
<ConfirmDialogChallenge />
</ConfirmDialogBody>
<ConfirmDialogFooter>
<ConfirmDialogCancel />
<ConfirmDialogAction onClick={() => setGatedOpen(false)}>
Delete workspace
</ConfirmDialogAction>
</ConfirmDialogFooter>
</ConfirmDialogContent>
</ConfirmDialog>
</div>
);
}Install — needs @hiip registered in components.json
bunx shadcn@latest add @hiip/confirm-dialogOr by URL, with the token in the query
bunx shadcn@latest add "https://ui.hiip.ai/r/confirm-dialog.json?token=$HIIP_REGISTRY_TOKEN"Both need a token — set one up.
Anatomy
Import from components/ui/hiip-ui/confirm-dialog.
<ConfirmDialog
open={open}
onOpenChange={setOpen}
challenge="hiip-ai/ui" {/* optional — omit it and there is no gate */}
>
<ConfirmDialogTrigger render={<Button variant="destructive" />}>
Delete
</ConfirmDialogTrigger>
<ConfirmDialogContent>
{/* the white card: the question */}
<ConfirmDialogBody>
<ConfirmDialogTitle />
<ConfirmDialogDescription />
{/* only with a `challenge` — unlocks the action when it matches */}
<ConfirmDialogChallenge />
</ConfirmDialogBody>
{/* the shell: the answers */}
<ConfirmDialogFooter>
<ConfirmDialogCancel /> {/* closes on its own */}
<ConfirmDialogAction pending={…} onClick={…}>
{/* the caller closes THIS one, once the request lands */}
</ConfirmDialogAction>
</ConfirmDialogFooter>
</ConfirmDialogContent>
</ConfirmDialog>API Reference
Generated from the source. Props forwarded to the underlying element are not listed.
ConfirmDialog
| Prop | Type | Default |
|---|---|---|
actionsRefA ref to imperative actions. - `unmount`: Manually unmounts the alert dialog. | RefObject<DialogRootActions | null> | undefined | — |
challengeA phrase the person must type out before the action unlocks — a project name, a hostname, `DELETE`. | string | undefined | — |
childrenThe content of the dialog. | ReactNode | PayloadChildRenderFunction<unknown> | — |
defaultOpenWhether the dialog is initially open. | boolean | undefined | — |
defaultTriggerIdID of the trigger that the dialog is associated with. | string | null | undefined | — |
handleA handle to associate the alert dialog with a trigger. | AlertDialogHandle<unknown> | undefined | — |
onOpenChangeEvent handler called when the alert dialog is opened or closed. | ((open: boolean, eventDetails: AlertDialogRootChangeEventDetails) => void) | undefined | — |
onOpenChangeCompleteEvent handler called after any animations complete when the dialog is opened or closed. | ((open: boolean) => void) | undefined | — |
openWhether the dialog is currently open. | boolean | undefined | — |
triggerIdID of the trigger that the dialog is associated with. | string | null | undefined | — |
ConfirmDialogTrigger
The control that opens the prompt.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: DialogTriggerState) => string | undefined) | undefined | — |
handleA handle to associate the trigger with an alert dialog. | AlertDialogHandle<unknown> | undefined | — |
idID of the trigger. | string | undefined | — |
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. | boolean | undefined | — |
payloadA payload to pass to the dialog when it is opened. | unknown | — |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTriggerState> | undef… | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: DialogTriggerState) => CSSProperties | undefined) | undefined | — |
ConfirmDialogContent
The tinted shell: it portals, dims the page and centres itself.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: DialogPopupState) => string | undefined) | undefined | — |
finalFocusDetermines the element to focus when the dialog is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previ… | boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | und… | — |
initialFocusDetermines the element to focus when the dialog is opened. | boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | unde… | — |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogPopupState> | undefined | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: DialogPopupState) => CSSProperties | undefined) | undefined | — |
ConfirmDialogBody
The white card: the question, and nothing that answers it.
No props of its own.
ConfirmDialogTitle
What is about to happen, as a question.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: DialogTitleState) => string | undefined) | undefined | — |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTitleState> | undefined | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: DialogTitleState) => CSSProperties | undefined) | undefined | — |
ConfirmDialogDescription
The consequence — what is lost, and whether it can come back.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: DialogDescriptionState) => string | undefined) | undefined | — |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogDescriptionState> | u… | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: DialogDescriptionState) => CSSProperties | undefined) | undefined | — |
ConfirmDialogChallenge
The typed gate: a labelled field that unlocks `ConfirmDialogAction` only once what is in it matches the root's `challenge`.
| Prop | Type | Default |
|---|---|---|
labelReplaces the default "Type <phrase> to confirm" line. | ReactNode | — |
ConfirmDialogFooter
The answers, on the shell rather than on the card.
No props of its own.
ConfirmDialogCancel
The safe answer.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: DialogCloseState) => string | undefined) | undefined | — |
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. | boolean | undefined | — |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogCloseState> | undefined | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: DialogCloseState) => CSSProperties | undefined) | undefined | — |
ConfirmDialogAction
The destructive answer, and the one part of this item that is NOT a `Close`.
| Prop | Type | Default |
|---|---|---|
classNameCSS class applied to the element, or a function that returns a class based on the component's state. | string | ((state: ButtonState) => string | undefined) | undefined | — |
focusableWhenDisabledWhether the button should be focusable when disabled. | boolean | undefined | — |
nativeButtonWhether the component renders a native `<button>` element when replacing it via the `render` prop. | boolean | undefined | — |
pendingIn flight: the label is replaced by a spinner and the button is inert. | boolean | undefined | false |
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, ButtonState> | undefined | — |
size | "default" | "sm" | "xs" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined | — |
styleStyle applied to the element, or a function that returns a style object based on the component's state. | CSSProperties | ((state: ButtonState) => CSSProperties | undefined) | undefined | — |
variant | "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined | — |
- Type
registry:ui- Pulls in
- button, inputshadcn installs these for you if they are missing.
- npm packages
@base-ui/react- Files written
- components/ui/hiip-ui/confirm-dialog.tsx