Skip to main content
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

Build docs developers (and LLMs) love