Undo toasts show an inline undo button with a countdown timer. If the user doesn’t click undo before time runs out, a confirmation event fires. This pattern is perfect for soft deletes and reversible actions.
How it works
When you create an undo toast, it displays a countdown timer and an undo button. You listen for a confirmation event that only fires if the user doesn’t click undo. This lets you delay destructive operations until you’re sure the user didn’t make a mistake.
Basic example
toast . undo ( 'Message deleted' , 'confirm-delete' , { id: 123 });
// Listen for confirmation — only fires if NOT undone
window . addEventListener ( 'confirm-delete' , ( e ) => {
fetch ( `/api/messages/ ${ e . detail . id } ` , { method: 'DELETE' });
});
The confirmation event only fires when the countdown expires. If the user clicks undo, the event never fires and the toast simply dismisses.
Full options
You can customize the undo toast with additional options:
toast . undo ({
title: 'Item archived' ,
event: 'confirm-archive' , // fires on countdown expiry
undoEvent: 'undo-archive' , // fires if user clicks undo (optional)
data: { id: 456 },
duration: 8000 , // countdown length in ms
type: 'info' , // toast type (default: warning)
});
Event data
The data object you provide gets passed to both the confirm and undo event listeners:
window . addEventListener ( 'confirm-archive' , ( e ) => {
console . log ( e . detail ); // { id: 456 }
// Proceed with archiving
});
window . addEventListener ( 'undo-archive' , ( e ) => {
console . log ( e . detail ); // { id: 456 }
// Restore the item
});
Livewire integration
Create undo toasts from your Livewire components:
$this -> dispatch ( 'toast-undo' , [
'title' => 'Item deleted' ,
'event' => 'confirm-delete' ,
'data' => [ 'id' => $id ],
'duration' => 5000 ,
]);
Then listen for the confirmation event in JavaScript:
window . addEventListener ( 'confirm-delete' , ( e ) => {
Livewire . dispatch ( 'permanent-delete' , { id: e . detail . id });
});
Use cases
Soft deletes Give users a chance to recover deleted items before permanent deletion
Archive actions Allow users to undo archiving of emails, documents, or records
Batch operations Let users cancel bulk actions they triggered by mistake
Status changes Provide undo for state transitions like marking items complete
Real-world example
Here’s a complete example with delete and undo handlers:
// Trigger the undo toast
const deleteMessage = ( messageId ) => {
toast . undo ({
title: 'Message deleted' ,
event: 'confirm-delete' ,
undoEvent: 'undo-delete' ,
data: { id: messageId },
duration: 5000 ,
type: 'warning' ,
});
};
// Handle permanent deletion
window . addEventListener ( 'confirm-delete' , async ( e ) => {
const { id } = e . detail ;
await fetch ( `/api/messages/ ${ id } ` , {
method: 'DELETE'
});
toast ({
type: 'success' ,
title: 'Message permanently deleted'
});
});
// Handle undo
window . addEventListener ( 'undo-delete' , ( e ) => {
toast ({
type: 'info' ,
title: 'Delete cancelled'
});
// Refresh your UI if needed
refreshMessages ();
});
Countdown duration
The default countdown is 5000ms (5 seconds). Adjust it based on your use case:
Quick actions — 3000ms (3 seconds)
Standard deletions — 5000ms (5 seconds)
Critical operations — 8000ms (8 seconds) or more
Don’t make the countdown too short. Users need enough time to read the toast and click undo if needed. We recommend at least 3 seconds.
Tips
Always use the undoEvent option if you need to restore state when users click undo
Keep the title short and action-focused: “Item deleted”, “Message archived”
Use the warning type (default) to grab attention
For Livewire, dispatch permanent actions back to your component instead of using JavaScript fetch
Test your undo flow thoroughly — users expect it to work perfectly