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.
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>
);
}
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>
);
}
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 & 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>
);
}
| 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. |
ConfirmDialogProps| Name | Type | Default | Description |
|---|---|---|---|
openrequired | boolean | — | Whether the query stands. Shown in: The one question, Ask before a commitment, Stay open while it runs and 3 more |
titlerequired | ReactNode | — | 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 |
description | ReactNode | — | 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 |
confirmLabel | string | the 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 |
cancelLabel | string | the 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 |
loading | boolean | false | Locks the actions while the confirmation is running. Shown in: Stay open while it runs |
ref | Ref<HTMLDialogElement> | — | Receives the component's root element. from RefPropShown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more |
| Name | Type | Default | Description |
|---|---|---|---|
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.
What umriss deliberately does not build, and why: ADR-0032.