Toastify Notification Stack with Undo — Free HTML CSS JS Snippet

Toastify Notification Stack with Undo · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

Genuinely reversible deletion
The data is not touched until the undo window expires.
Callback-driven permanence
Toastify's own timer completion is what finalizes the delete.
Real interactive toast content
A custom DOM node enables an actual clickable Undo button.
Independent per-item undo state
A pending map, not a single global "last deleted" slot.
Immediate visual feedback
The item disappears instantly, staged deletion happens after.
Correctly scoped click handling
The Undo button has its own listener, separate from the toast.

About this UI Snippet

Toastify Notification Stack with Undo — Deletion That Isn't Actually Immediate

Screenshot of the Toastify Notification Stack with Undo snippet rendered live

A real "undo delete" needs the delete to not actually happen yet — otherwise "undo" would need to somehow reverse a completed action, which is a much harder problem than simply not doing it in the first place. This snippet stages every delete behind a pending-state map and a toast's own timer, so Undo just means "never actually go through with it."

Deletion is staged in a pending map, not applied to the data immediately

Clicking delete adds the email's id to pendingDeletes and re-renders, which is what makes it disappear from the list right away — but the email is still sitting in the real EMAILS array the whole time. Nothing has actually been removed yet; only the render filter is currently hiding it.

Toastify's own callback is where deletion becomes real

Toastify's callback option runs when a toast's timer finishes and it's automatically dismissed — *not* when it's shown. This snippet's callback is the only place that actually splices the email out of the EMAILS array for good, which means the deletion only becomes permanent once the undo window has genuinely expired without being used.

Undo works by preventing that callback from mattering, not by reversing anything

Clicking "Undo" clears the pending flag, re-renders (bringing the email back into view), and calls toast.hideToast() to dismiss the toast early. Toastify's callback still isn't guaranteed to run in every version/path when a toast is manually hidden versus timing out naturally — but because the pending flag was already cleared and the render already restored the email, nothing else needs to happen for undo to be complete. There was never a real deletion to reverse.

A custom `node` instead of a text string is what allows a real Undo button

Toastify's basic text option only supports plain text content. Passing a pre-built node — a real DOM element containing both a message span and a button — is what makes an interactive, clickable Undo control inside the toast possible, with the button's own click listener attached directly to that node before the toast is even shown.

Reusing it

This staged-delete-plus-undo-window pattern generalizes to any reversible destructive action — archiving, removing a list item, discarding a draft — swap the specific data operations for whatever "delete" and "restore" actually mean for your data.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out staged-deletion state management from scratch. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the email is only added to a pending-state tracking object rather than removed from the real data immediately, and how Toastify's callback option (tied to the toast's own timer completing) is what makes the deletion permanent only after the undo window has genuinely expired. The same assistant can help optimize it — ask whether keying pending deletions by id in a plain object scales fine for a list with hundreds of items, or whether a different data structure would be more efficient. It's also useful for extending the effect: ask it to support undoing multiple deletions with one combined "Undo all" toast if several are deleted in quick succession, persist the staged deletion across a page reload using sessionStorage, or add a real backend delete call that only fires inside the callback once the undo window has passed. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a list with a delete-with-undo pattern (similar to Gmail's email deletion) using the Toastify-js library (load Toastify's CSS and JS from a CDN, no other library), in plain HTML, CSS, and JavaScript.

Requirements:
- Render a list of at least five sample items (such as emails, each with a subject and sender), each with a delete button.
- Clicking an item's delete button must immediately hide that item from the visible list, but must not actually remove it from the underlying data yet — implement this as a staged/pending deletion tracked separately from the real data, not an immediate data mutation.
- Show a toast notification with a custom message naming the deleted item and a real, clickable "Undo" button (not just plain text) that stays visible for a fixed duration (such as 5 seconds).
- If the undo button is clicked before the toast's duration expires, the item must reappear in the list in its original position, and the toast should dismiss early — with no actual data ever having been deleted in the first place.
- If the undo window expires without the undo button being clicked, the item must then actually be permanently removed from the underlying data, triggered specifically by the toast's own natural dismissal (its timer completing), not by a separately managed timer duplicating that duration.
- Support deleting multiple items in quick succession, each with its own fully independent pending state, undo toast, and timer, so undoing or waiting out one deletion has no effect on any other pending deletion.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="tn-wrap">
  <div class="tn-title">Inbox</div>
  <ul class="tn-list" id="tnList"></ul>
  <p class="tn-hint">Deleting an email shows an undo toast for 5 seconds</p>
</div>

Step by step

How to Use

  1. 1
    Add the Toastify CDNLoad toastify.min.css and toastify.min.js before the snippet's JS runs.
  2. 2
    Paste HTML, CSS, and JSFive sample emails render in a list.
  3. 3
    Click the × on any emailIt disappears and an undo toast appears at the bottom.
  4. 4
    Click UNDO within 5 secondsThe email reappears in the list exactly where it was.
  5. 5
    Delete another email and waitAfter 5 seconds, the deletion becomes permanent.
  6. 6
    Delete several emails quicklyEach gets its own independent undo window.

Real-world uses

Common Use Cases

Email and messaging clients
The exact Gmail-style delete-with-undo pattern.
List and table row deletion
Any destructive list action worth a safety net.
File and document management
Pair with the confirmation dialog set elsewhere in this collection for higher-stakes deletions.
Shopping cart item removal
Let users recover an accidentally removed item.
Draft and note discarding
Reversible removal for low-friction content tools.
Learning Toastify customization
A clear reference for custom node content and callbacks.

Got questions?

Frequently Asked Questions

No — clicking delete only adds that email's id to a pendingDeletes tracking object and re-renders the list, which filters out anything in that pending state. The email object itself remains untouched in the real EMAILS array the entire time the undo toast is showing; nothing is actually removed from the underlying data until the undo window expires.

Toastify's callback option is a function that runs specifically when a toast's own timer completes and it dismisses itself naturally (not when it's shown, and not necessarily when manually hidden early). This snippet's callback is the only code that actually filters the email out of the real EMAILS array — meaning the deletion only becomes permanent once the 5-second undo window has fully elapsed without the user clicking Undo.

Since the email was never really removed from EMAILS — only hidden via the pendingDeletes flag — undo simply clears that flag, re-renders the list (which now shows the email again, reading from the same array it was always in), and hides the toast early. There's no data to restore or reverse, because the "deletion" was staged rather than applied from the start.

Toastify's basic text option can only display a plain string — it has no mechanism for embedding an interactive element like a button inside the toast. Passing a pre-built DOM node (containing both a text span and a real button element) via the node option is what makes it possible to have an actual clickable Undo control inside the toast, with its own click event listener attached to that button before the toast is shown.

Each deletion creates its own independent entry in pendingDeletes (keyed by that email's id) and its own separate Toastify instance with its own 5-second timer. This means deleting several emails quickly gives each one its own independent undo window and its own toast, rather than one deletion's undo accidentally affecting or canceling a different email's pending deletion.