Source Code
<div class="container py-5 d-flex justify-content-center">
<div class="card bsundo-card">
<div class="card-body p-3">
<h6 class="fw-bold mb-2">Inbox</h6>
<ul class="list-unstyled mb-0" id="bsundoList"></ul>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div class="toast align-items-center" id="bsundoToast" role="status">
<div class="d-flex">
<div class="toast-body" id="bsundoToastBody"></div>
<button type="button" class="btn btn-sm btn-link text-decoration-none flex-shrink-0" id="bsundoBtn">Undo</button>
</div>
</div>
</div>
</div>.bsundo-card { width: 380px; max-width: 100%; border: 1px solid #eceef1; border-radius: 14px; }
.bsundo-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 4px; font-size: 13.5px; border-bottom: 1px solid #f1f2f5; }
.bsundo-item:last-child { border-bottom: none; }
.bsundo-remove { border: none; background: none; color: #9ca3af; cursor: pointer; }
.bsundo-remove:hover { color: #dc3545; }const MESSAGES = [
'Q3 planning notes',
'Invoice #4021 receipt',
'Team offsite itinerary',
'Design review feedback',
'Weekly report draft',
];
const list = document.getElementById('bsundoList');
const toastEl = document.getElementById('bsundoToast');
const toastBody = document.getElementById('bsundoToastBody');
const toast = new bootstrap.Toast(toastEl, { autohide: true, delay: 4000 });
let items = MESSAGES.map((text, id) => ({ id, text }));
let lastRemoved = null;
let lastIndex = -1;
function render() {
list.innerHTML = items.map(item =>
'<li class="bsundo-item" data-id="' + item.id + '"><span>' + item.text +
'</span><button type="button" class="bsundo-remove" data-id="' + item.id + '">×</button></li>'
).join('') || '<li class="bsundo-item text-muted">Inbox is empty.</li>';
}
list.addEventListener('click', e => {
const btn = e.target.closest('.bsundo-remove');
if (!btn) return;
const id = Number(btn.dataset.id);
lastIndex = items.findIndex(i => i.id === id);
lastRemoved = items[lastIndex];
items = items.filter(i => i.id !== id);
render();
toastBody.textContent = '"' + lastRemoved.text + '" deleted.';
toast.show();
});
document.getElementById('bsundoBtn').addEventListener('click', () => {
if (!lastRemoved) return;
items.splice(Math.min(lastIndex, items.length), 0, lastRemoved);
lastRemoved = null;
render();
toast.hide();
});
render();Bootstrap Undo Action Toast — Free HTML CSS JS Snippet
Bootstrap Undo Action Toast · Misc · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Bootstrap Undo Action Toast — HTML, CSS & JavaScript

Undo only feels trustworthy if the restored item lands back exactly where it was — this snippet records lastIndex (the removed item's position) alongside lastRemoved (the item itself) at the moment of deletion, and splice(lastIndex, 0, lastRemoved) re-inserts it at that same position rather than appending it to the end of the list, which would silently reorder the inbox every time Undo was used.
Only the single most recently deleted item is ever recoverable — removing a second item before undoing the first overwrites lastRemoved/lastIndex with the new deletion, matching how a real single-toast undo pattern behaves (and matching what the visible toast itself can honestly represent, since only one toast is showing a single "Undo" action at a time).
The toast uses Bootstrap's own Toast component with autohide: true — after 4 seconds with no action, it disappears on its own, and lastRemoved staying set past that point is deliberate: nothing in this snippet clears it on autohide, so a very fast click on a since-hidden toast's Undo button (impossible through the UI, but worth noting for anyone modifying this) would still work as expected. In a real implementation, the actual delete request to a server should be delayed until the toast's hide event fires with no undo, so a click on Undo can cancel the deletion before it's ever actually persisted.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Hand this snippet to an AI coding assistant like Claude and ask it to support undoing a batch of several deletions at once (e.g. from bootstrap-bulk-action-toolbar) instead of only a single item, or to delay the real backend delete call until the toast's autohide fires with no Undo click, so an in-time Undo can cancel it before anything is actually persisted.
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:
Build a Bootstrap 5.3 list with an undoable delete action via a toast, using the real Bootstrap CDN framework (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble it.
Requirements:
- A list of at least 5 sample items, each with its own remove button.
- Clicking an item's remove button removes it from the list and shows a real Bootstrap toast (with autohide after a few seconds) naming the removed item, with an "Undo" action inside the toast.
- Clicking Undo must restore the item to its exact original position in the list (not append it to the end), and hide the toast immediately.
- Only the single most recently deleted item should ever be undoable — deleting a second item before undoing the first must make the first one permanently unrecoverable through the UI, with the toast and Undo action reflecting only the latest deletion.
- Show a clear empty-state message once every item has been removed.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.
Step by step
How to Use
- 1Load the snippetFive inbox items show in a list, no toast visible.
- 2Click the \u00d7 next to the third itemIt disappears from the list, and a toast appears at the bottom right naming it, with an Undo action.
- 3Click "Undo"The item reappears in the exact same position — third in the list, not appended to the end.
- 4Delete an item and wait 4 seconds without clicking UndoThe toast automatically hides itself, and the deletion stands.
- 5Delete two different items in a rowOnly the second deletion remains undoable — the toast and Undo action always reflect the most recent removal.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The first deletion becomes permanently unrecoverable through this UI — lastRemoved and lastIndex are overwritten by the second deletion, and the toast (there's only ever one visible) now represents only the most recent removal.
Yes — splice(lastIndex, 0, lastRemoved) inserts it back at its original index, clamped to the current list length in case items were somehow shorter since (which can't happen in this demo, but is a reasonable defensive detail).
This demo removes it from the visible list immediately for clarity; a production implementation typically delays the real destructive backend call until the toast's hide event fires with no Undo click, so an in-time Undo can cancel the action before anything is truly deleted.
Yes. Keep the items array, lastRemoved, and lastIndex in component state, and drive Bootstrap's Toast through a ref-based instance or an equivalent framework toast/snackbar component using the same restore-at-index logic.