Skip to content
Devix Open Source
UI component v1.0.0 MIT Alpha

Confirm Dialog

Promise-based dialogs on native <dialog>: confirm, prompt and type-to-confirm.

npm install @devix-labs/confirm-dialog
Vanilla JS React Vue Svelte
Confirm, alert, prompt Open

Promise-based dialogs built on the element the browser already ships: showModal() gives a real focus trap, a real inert background, Escape handling, the top layer and focus returning to the trigger — none of it re-implemented. A danger dialog is an alertdialog that opens on Cancel, ignores the backdrop and can demand the word typed out. Fields come with per-field validation and typed values, and onConfirm may be async: the dialog stays open, shows it is busy, and puts a thrown error inline.

What you get

The browser does the hard part

A native <dialog> with showModal(): focus trapped, background inert for screen readers too, Escape handled, top layer, focus returned to the trigger.

Destructive by design

A danger dialog is an alertdialog, opens on Cancel, ignores backdrop clicks, and can require “DELETE” typed out before the button wakes up.

Async confirms

onConfirm can do the work: the dialog stays open and busy, and a thrown error is shown inside it rather than lost behind a closed modal.

Prompts are forms

Text, number, email, textarea and checkbox fields with per-field validation, the first problem focused, and values handed back typed.

3.4 KB, no dependencies

A quarter of SweetAlert2 with its CSS, and no jQuery or Bootstrap the way bootbox needs.

Wears your design system

CSS variables for every value, classNames on every part, your icons and words, and a @layer stylesheet so your CSS wins without !important.

Confirm Dialog — overview

Why it exists

window.confirm blocks the page, cannot be styled or translated, and offers exactly two words. The usual replacement is SweetAlert2 at 20 kB with its own focus trap. Meanwhile the browser has shipped <dialog>: a real modal with a real focus trap, a real top layer and a real inert background.

What it does differently

  • The browser does the hard part. showModal() means focus containment, Escape, the top layer and the inert background are native — and focus returns to the trigger afterwards.
  • 3.4 kB gzip, no dependencies, a quarter of SweetAlert2.
  • Destructive by design. danger makes it an alertdialog, focuses Cancel, ignores the backdrop, and can require the words to be typed.
  • Async confirms. onConfirm can do the work: the dialog stays open and busy, and a thrown error is shown inline rather than lost.
  • Prompts are forms. Several fields, per-field validation, typed values back.

Shape of the package

Entry What
@devix/confirm-dialog confirm, confirmDanger, alert, prompt, dialog, configure
@devix/confirm-dialog/react, /vue, /svelte the same functions, plus useDialogs()
@devix/confirm-dialog/styles.css, /styles.layer.css the theme, plain or inside @layer devix

Not in 1.0

Drawers and sheets, multi-step wizards and a toast mode: the first two are their own products, and toasts are @devix/toast.

How it compares

Questions

Why not just use window.confirm?

It blocks the page, cannot be styled or translated, offers exactly two words, and browsers are free to suppress it — some already do in cross-origin iframes and on repeat calls. This is a promise you can await, with your words and your styling.

Is the focus trap reliable?

It is the browser's. showModal() traps focus, makes the rest of the page inert for pointers and screen readers alike, handles Escape and returns focus to whatever opened the dialog. Nothing here re-implements any of it.

Can I keep the dialog open while saving?

Yes — that is what onConfirm is for. Return a promise and the dialog stays open with a busy button; throw and the message appears inside it, so the user can fix the problem without reopening anything.

Can I ask for more than yes or no?

Pass fields for a small form, or actions for your own set of buttons. Values come back typed, and each field can validate itself.

Does it stack?

Yes. Dialogs nest natively — a confirm inside an onConfirm opens above the first one, and the one below stays inert until it closes.

How do I make it look like my app?

Every value is a CSS variable, every part takes your own classes (globally through configure() or per dialog), icons and strings are replaceable, and the stylesheet also ships inside @layer devix.