Migrating from SweetAlert2 to toastory
Modernize legacy SweetAlert2 alerts, prompts, and modal dialogs with Apple-style frosted glass, zero runtime dependencies, and strict TypeScript safety.
SweetAlert2 has powered modal dialogs for over a decade. However, its legacy jQuery roots, 40 KB+ footprint, global state mutations, and dated skeuomorphic aesthetics make it a heavy fit for modern web apps.
toastory replaces SweetAlert2 with modern Apple frosted glass aesthetics, full WCAG 2.2 AA accessibility, focus trapping, and zero runtime dependencies.
---
Why Modernize SweetAlert2?
- Modern Frosted Glass Aesthetic: Specular highlight borders and real physical blur instead of flat gray boxes.
- Micro Bundle Size: Full web bundle under 22 KB gzip (including toasts, dialogs, and sheets), compared to SweetAlert2 alone at 42 KB+.
- Strict TypeScript Safety: No
anytypes; full autocompletion for 16 input types and return result objects. - Integrated Feedback System: One package covers both floating notifications and blocking modal dialogs.
---
Option & Result Mapping Table
| SweetAlert2 API | toastory Equivalent | Notes |
|---|---|---|
Swal.fire({ title, text, icon }) | ui.alert({ title, text, tone }) | Accessible alert with focus trap. |
Swal.fire({ showCancelButton: true }) | ui.confirm({ ... }) | Two-button confirmation modal. |
Swal.fire({ input: 'text' }) | ui.prompt({ input: 'text' }) | 16 native input types supported. |
Swal.fire({ input: 'select', inputOptions }) | ui.prompt({ input: 'select', options }) | Dropdown select support. |
Swal.queue(steps) | ui.steps(stepList) | First-class multi-step flow with back button. |
Swal.showLoading() | ui.loading({ text }) | Indeterminate or progress overlay. |
result.isConfirmed | result.isConfirmed | Identical boolean flag. |
result.isDenied | result.isDenied | Identical boolean flag. |
result.isDismissed | result.isDismissed | Identical boolean flag. |
result.value | result.value | Typed value of prompt input. |
result.dismiss | result.dismiss | Reason: 'cancel', 'backdrop', 'close', 'esc', 'timer'. |
preConfirm | preConfirm: async (val, handle) | Async confirmation hook with automatic button spinner. |
---
Side-by-Side Implementation Examples
#### 1. Dangerous Destructive Confirmation
// Before (SweetAlert2)
const result = await Swal.fire({
title: 'Are you sure?',
text: 'You will not be able to recover this file!',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel',
confirmButtonColor: '#d33',
});
if (result.isConfirmed) {
Swal.fire('Deleted!', 'Your file has been deleted.', 'success');
}// After (toastory)const result = await ui.confirm({ title: 'Are you sure?', text: 'You will not be able to recover this file!', tone: 'danger', confirmButton: { label: 'Yes, delete it!' }, cancelButton: { label: 'Cancel' }, });
if (result.isConfirmed) {
toast.success('Your file has been deleted.');
}
`
---
#### 2. Input Prompt with Validation and Async Lock
// Before (SweetAlert2)
const { value: email } = await Swal.fire({
title: 'Input email address',
input: 'email',
inputPlaceholder: 'Enter your email',
inputValidator: (value) => {
if (!value) return 'You need to write something!';
},
showCancelButton: true,
showLoaderOnConfirm: true,
preConfirm: async (val) => {
await sendInvite(val);
},
});// After (toastory)
const res = await ui.prompt({
title: 'Input email address',
input: 'email',
placeholder: 'Enter your email',
validate: (value) => {
if (!value) return 'You need to write something!';
return undefined; // valid
},
preConfirm: async (val) => {
await sendInvite(val as string);
},if (res.isConfirmed) {
toast.success(Invitation sent to ${res.value});
}
`
Want to test these options live?
Launch the interactive playground to experiment with themes, positions, and live generators.