Confirm Dialog
<x-confirm-dialog /> is a styled replacement for the browser's window.confirm(). Render it once in your layout and every wire:confirm in your application opens it automatically: the Livewire action only runs after the user confirmed, and cancelling does nothing, exactly as before.
Outside of Livewire, the $confirm Alpine magic and window.bcConfirm() open the same dialog and resolve with true or false.
Setup
Add the component once to your main layout, next to <livewire-toast />. Only one dialog may exist per page, because its elements use fixed IDs.
Without the component on the page nothing breaks: every confirmation falls back to the browser's native dialog.
1<html> 2<body> 3 <!-- .... --> 4 5 @persist('toast') 6 <livewire-toast /> 7 @endpersist 8 9 @persist('confirm-dialog') 10 <x-confirm-dialog /> 11 @endpersist 12</body>13</html>
Basic Usage
Keep using wire:confirm the way Livewire documents it. With the dialog in the layout, the message appears in the styled dialog with the default title, and the action runs once the user confirms. Try it: the button below triggers a Livewire action that shows a toast.
wire:confirm.prompt keeps Livewire's native prompt, because the dialog has no text input.
1<x-button type="danger" wire:click="confirmed" wire:confirm="Do you really want to delete this user?">Delete user</x-button>
Types
Pick a type with a modifier: wire:confirm.danger, .primary, .info or .success. Without a modifier the dialog uses warning. The type sets the icon, its colour and the colour of the confirm button. A data-confirm-type attribute wins over the modifier.
1<x-button type="white" wire:click="confirmed" wire:confirm="Unsaved changes will be lost.">Warning</x-button>2<x-button type="white" wire:click="confirmed" wire:confirm.danger="The user will be deleted permanently.">Danger</x-button>3<x-button type="white" wire:click="confirmed" wire:confirm.primary="Send the invitation now?">Primary</x-button>4<x-button type="white" wire:click="confirmed" wire:confirm.info="The export runs in the background.">Info</x-button>5<x-button type="white" wire:click="confirmed" wire:confirm.success="Mark all tasks as done?">Success</x-button>
Title, Button Labels and Checkbox
data-confirm-title, data-confirm-button and data-confirm-cancel replace the default texts. data-confirm-checkbox adds a checkbox with the given label, and the confirm button stays disabled until it is ticked. Write \n in the message for a line break.
1<x-button 2 type="danger" 3 wire:click="confirmed" 4 wire:confirm.danger="The user and all of their data will be deleted.\nThis cannot be undone." 5 data-confirm-title="Delete user" 6 data-confirm-button="Yes, delete" 7 data-confirm-cancel="Keep user" 8 data-confirm-checkbox="I understand that this cannot be undone" 9>10 Delete user11</x-button>
The confirm Prop of x-button
<x-button> accepts a confirm prop that writes those attributes for you. Pass a string for the message, or an array with the keys message, title, type, button, cancel and checkbox. The prop only renders wire:confirm, so the button still needs its wire:click.
1<x-button type="danger" wire:click="confirmed" :confirm="[ 2 'type' => 'danger', 3 'title' => 'Delete user', 4 'message' => 'The user and all of their data will be deleted.', 5 'button' => 'Yes, delete', 6 'cancel' => 'Keep user', 7 'checkbox' => 'I understand that this cannot be undone', 8]">Delete user</x-button> 9 10<x-button wire:click="confirmed" confirm="Archive this project?">Archive</x-button>
Forms
wire:confirm on a form with wire:submit asks before the form is submitted.
1<form wire:submit="save" wire:confirm.primary="Publish the article and notify all subscribers?" class="d-flex flex-column gap-3">2 <x-input label="Title" wire:model="articleTitle" />3 <x-button as="submit" type="primary">Publish</x-button>4</form>
Alpine.js
The $confirm magic opens the dialog from any Alpine expression and returns a promise that resolves with true when the user confirms and false when they cancel, press Escape or click the backdrop. Pass a string to use it as the message.
1<x-button2 type="primary"3 x-on:click="$confirm({ title: 'Publish article?', message: 'It will be visible to everyone.', type: 'primary' })4 .then(confirmed => confirmed && toast.success('Published', 'The promise resolved with true.'))"5>6 Publish7</x-button>
JavaScript
window.bcConfirm() is the same function for plain JavaScript. Opening a dialog while another one is open cancels the first one, which resolves with false.
The bc-confirm window event, also available as $this->dispatch('bc-confirm', ...) from Livewire, only opens the dialog. Its answer is not passed back, so use the promise whenever you need to know what the user chose.
1<script> 2 // Resolves with true (confirmed) or false (cancelled or closed). 3 const confirmed = await window.bcConfirm({ 4 title: 'Leave this page?', 5 message: 'Your draft has not been saved yet.', 6 type: 'warning', // warning, danger, primary, info, success 7 checkbox: null, // label of a checkbox that must be ticked first 8 confirmText: 'Leave', 9 cancelText: 'Stay',10 });11 12 // A plain string is used as the message.13 window.bcConfirm('Continue?').then(confirmed => { /* ... */ });14 15 // Fire and forget: opens the dialog, the answer is not passed back.16 window.dispatchEvent(new CustomEvent('bc-confirm', { detail: { title: 'Heads up', type: 'info' } }));17</script>
Options
The same options are available in every way of opening the dialog.
| Attribute | Array key | JavaScript | Description |
|---|---|---|---|
| wire:confirm="…" | message | message | The text below the title. \n starts a new line. |
| wire:confirm.<type> or data-confirm-type | type | type | warning (default), danger, primary, info or success. |
| data-confirm-title | title | title | The heading. Defaults to the translation of basic-components::confirm-dialog.title. |
| data-confirm-checkbox | checkbox | checkbox | Label of a checkbox that has to be ticked before the user can confirm. |
| data-confirm-button | button | confirmText | Label of the confirm button. |
| data-confirm-cancel | cancel | cancelText | Label of the cancel button. |
The component itself takes two attributes: type sets the default type for this page and :override-wire-confirm="false" leaves wire:confirm to the browser while $confirm keeps working.
Configuration
The defaults live under confirm-dialog in config/basic-components.php. Leave the texts on null to use the translations that ship in English and German (basic-components::confirm-dialog.title, .confirm and .cancel).
1// config/basic-components.php 2 3'confirm-dialog' => [ 4 // false keeps Livewire's native browser dialog for every wire:confirm 5 'override-wire-confirm' => true, 6 7 // used when neither wire:confirm.<type> nor data-confirm-type is given 8 'default-type' => 'warning', 9 10 // every type needs an icon and the button type of the confirm button11 'types' => [12 'warning' => ['icon' => 'exclamation-triangle', 'button' => 'warning'],13 'danger' => ['icon' => 'exclamation-circle', 'button' => 'danger'],14 'primary' => ['icon' => 'question-mark-circle', 'button' => 'primary'],15 'info' => ['icon' => 'information-circle', 'button' => 'primary'],16 'success' => ['icon' => 'check-circle', 'button' => 'success'],17 ],18 19 // null uses the translations basic-components::confirm-dialog.title|confirm|cancel20 'title' => null,21 'confirm-text' => null,22 'cancel-text' => null,23],
Custom Types and Styling
Each type needs a colour entry in the $confirm-dialog-types SCSS map. If you compile the styles from your own copy of the variables, add the map there, otherwise the stylesheet stops compiling. A custom type needs both: a map entry here and an entry in confirm-dialog.types in the config.
1// Your own copy of the variables file needs this map, otherwise the stylesheet does not compile. 2$confirm-dialog-width: 27rem; 3 4$confirm-dialog-types: ( 5 'warning': ('color': $color-amber-600, 'background-color': $color-amber-50, 'ring': $color-amber-200), 6 'danger': ('color': $color-red-600, 'background-color': $color-red-50, 'ring': $color-red-200), 7 'primary': ('color': $color-primary-500, 'background-color': $color-primary-50, 'ring': $color-primary-200), 8 'info': ('color': $color-blue-600, 'background-color': $color-blue-50, 'ring': $color-blue-200), 9 'success': ('color': $color-green-600, 'background-color': $color-green-50, 'ring': $color-green-200),10 11 // a custom type also needs an entry in config('basic-components.confirm-dialog.types')12 'billing': ('color': $color-violet-600, 'background-color': $color-violet-50, 'ring': $color-violet-200),13);
Behaviour and Accessibility
- The dialog is announced as an alertdialog. Focus stays inside it while it is open and returns to the element that opened it afterwards.
- Escape and a click on the backdrop cancel. The checkbox toggles with Space and Enter.
- It sits above open modals. Inside a modal, Escape closes the dialog first and leaves the modal open.
- Page scrolling is locked while it is open.