hiip/ui
14 items

Components / Feedback

Confirm Dialog

The gate in front of something irreversible: a question, its consequence, and two unequal answers.

demos.tsx
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&rsquo;t
              be undone &mdash; 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-dialog

Or 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

PropTypeDefault
actionsRef

A ref to imperative actions. - `unmount`: Manually unmounts the alert dialog.

RefObject<DialogRootActions | null> | undefined—
challenge

A phrase the person must type out before the action unlocks — a project name, a hostname, `DELETE`.

string | undefined—
children

The content of the dialog.

ReactNode | PayloadChildRenderFunction<unknown>—
defaultOpen

Whether the dialog is initially open.

boolean | undefined—
defaultTriggerId

ID of the trigger that the dialog is associated with.

string | null | undefined—
handle

A handle to associate the alert dialog with a trigger.

AlertDialogHandle<unknown> | undefined—
onOpenChange

Event handler called when the alert dialog is opened or closed.

((open: boolean, eventDetails: AlertDialogRootChangeEventDetails) => void) | undefined—
onOpenChangeComplete

Event handler called after any animations complete when the dialog is opened or closed.

((open: boolean) => void) | undefined—
open

Whether the dialog is currently open.

boolean | undefined—
triggerId

ID of the trigger that the dialog is associated with.

string | null | undefined—

ConfirmDialogTrigger

The control that opens the prompt.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: DialogTriggerState) => string | undefined) | undefined—
handle

A handle to associate the trigger with an alert dialog.

AlertDialogHandle<unknown> | undefined—
id

ID of the trigger.

string | undefined—
nativeButton

Whether the component renders a native `<button>` element when replacing it via the `render` prop.

boolean | undefined—
payload

A payload to pass to the dialog when it is opened.

unknown—
render

Allows 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…—
style

Style 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.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: DialogPopupState) => string | undefined) | undefined—
finalFocus

Determines 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…—
initialFocus

Determines the element to focus when the dialog is opened.

boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | unde…—
render

Allows 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—
style

Style 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.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: DialogTitleState) => string | undefined) | undefined—
render

Allows 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—
style

Style 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.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: DialogDescriptionState) => string | undefined) | undefined—
render

Allows 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…—
style

Style 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`.

PropTypeDefault
label

Replaces 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.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: DialogCloseState) => string | undefined) | undefined—
nativeButton

Whether the component renders a native `<button>` element when replacing it via the `render` prop.

boolean | undefined—
render

Allows 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—
style

Style 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`.

PropTypeDefault
className

CSS class applied to the element, or a function that returns a class based on the component's state.

string | ((state: ButtonState) => string | undefined) | undefined—
focusableWhenDisabled

Whether the button should be focusable when disabled.

boolean | undefined—
nativeButton

Whether the component renders a native `<button>` element when replacing it via the `render` prop.

boolean | undefined—
pending

In flight: the label is replaced by a spinner and the button is inert.

boolean | undefinedfalse
render

Allows 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—
style

Style 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