ConfirmDialog

A small modal window with one question and two buttons, for the moment before something that cannot be undone (also called a confirmation). Reach for it before deleting, rejecting or closing for good.

import { ConfirmDialog } from "@umriss-ui/core";

Use it only where the step cannot be taken back: a question before every action gets clicked away, and then it no longer protects the one that mattered.

It does not close itself on onConfirm, so you can show loading while the request runs. Cancelling, by Escape, the cross or the second button, calls onClose.

Examples

The one question

The description says what cannot be undone, and confirmLabel names the action – “Delete” says what happens, “OK” does not.

import { useState } from "react";
import { Button, ConfirmDialog, Stack, Text } from "@umriss-ui/core";

export default function TheOneQuestion() {
  const [open, setOpen] = useState(false);
  const [state, setState] = useState("Draft invoice INV-26-0319 exists.");

  return (
    <Stack gap={3} align="flex-start">
      <Button variant="danger" onClick={() => setOpen(true)}>
        Delete the draft
      </Button>
      <Text size="xs" tone="muted">
        {state}
      </Text>
      <ConfirmDialog
        open={open}
        onClose={() => setOpen(false)}
        onConfirm={() => {
          setState("Draft invoice INV-26-0319 is deleted.");
          setOpen(false);
        }}
        tone="danger"
        title="Delete draft invoice INV-26-0319?"
        description="Its three lines and the attached delivery note go with it. This cannot be undone."
        confirmLabel="Delete"
      />
    </Stack>
  );
}

Ask before a commitment

Not every irreversible step destroys something: the primary tone asks before closing a month, which then accepts no postings.

import { useState } from "react";
import { Button, ConfirmDialog, Stack, Text } from "@umriss-ui/core";

export default function ACommitment() {
  const [open, setOpen] = useState(false);
  const [closed, setClosed] = useState(false);

  return (
    <Stack gap={3} align="flex-start">
      <Button variant="primary" disabled={closed} onClick={() => setOpen(true)}>
        Close February
      </Button>
      <Text size="xs" tone="muted">
        February 2026 is {closed ? "closed" : "open for postings"}.
      </Text>
      <ConfirmDialog
        open={open}
        onClose={() => setOpen(false)}
        onConfirm={() => {
          setClosed(true);
          setOpen(false);
        }}
        tone="primary"
        title="Close February 2026?"
        description="Actuals for February can no longer be posted or corrected; late invoices go to March."
        confirmLabel="Close the month"
      />
    </Stack>
  );
}

Stay open while it runs

The dialog does not close itself on onConfirm; set loading while the request runs and close it when it is done.

import { useEffect, useState } from "react";
import { Button, ConfirmDialog, Stack, Text } from "@umriss-ui/core";

export default function WhileItRuns() {
  const [open, setOpen] = useState(false);
  const [running, setRunning] = useState(false);
  const [rejected, setRejected] = useState(false);

  useEffect(() => {
    if (!running) return;
    const timer = setTimeout(() => {
      setRunning(false);
      setRejected(true);
      setOpen(false);
    }, 1500);
    return () => clearTimeout(timer);
  }, [running]);

  return (
    <Stack gap={3} align="flex-start">
      <Button variant="danger" disabled={rejected} onClick={() => setOpen(true)}>
        Reject the invoice
      </Button>
      <Text size="xs" tone="muted">
        INV-26-0317 from Kettering &amp; Shaw Events is {rejected ? "rejected" : "awaiting approval"}.
      </Text>
      <ConfirmDialog
        open={open}
        onClose={() => setOpen(false)}
        onConfirm={() => setRunning(true)}
        loading={running}
        tone="danger"
        title="Reject invoice INV-26-0317?"
        description="The supplier is told by e-mail and must send a new invoice."
        confirmLabel="Reject"
      />
    </Stack>
  );
}

When to use something else

Keyboard

Key Action
Tab / Shift+Tab Moves between the controls inside; focus does not leave the window.
Escape Asks to close: onClose is called, and the window goes when you close it.
Enter or Space Presses the focused button.

API

ConfirmDialogProps

NameTypeDefaultDescription
openrequiredboolean—Whether the query stands.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

titlerequiredReactNode—The question, as a statement: "Delete invoice 2041?".

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

descriptionReactNode—What about it cannot be taken back. The most important line of the dialog.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

confirmLabelstringthe wording's "Confirm"Label of the agreeing button. Better is the verb of the action: "Delete" says what is about to happen.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

cancelLabelstringthe wording's "Cancel"Label of the cancelling button.

Shown in: Guard unsaved changes (Modal)

tone"primary" | "danger""primary""danger" for destructive actions.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 2 more

loadingbooleanfalseLocks the actions while the confirmation is running.

Shown in: Stay open while it runs

refRef<HTMLDialogElement>—Receives the component's root element. from RefProp

Shown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more

Events

NameTypeDefaultDescription
onCloserequired() => void—Cancelling: Escape, cross, backdrop or the second button.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

onConfirmrequired() => void—Agreeing. The dialog does not close itself from that – whoever wants to show a confirmation in progress still needs it.

Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more

Also takes every attribute of <dialog> – without children, onClose, open, title.

Known limits

What umriss deliberately does not build, and why: ADR-0032.

Every page of @umriss-ui/core

Getting started

Customising

Layout

Typography

Actions

Forms

Choosing

Dates and times

Feedback

Overlays

Navigation

Data display

API index