Migrating from Sonner to toastory
Complete drop-in migration guide from Sonner to toastory with side-by-side API mappings, option comparisons, and extended feature guides.
If you are currently using Sonner, upgrading to toastory gives you full drop-in compatibility for all toast methods while adding a complete modal dialog toolkit, multi-step wizards, bottom sheets, and multi-framework support—all with zero runtime dependencies.
---
Why Migrate from Sonner?
1. Complete UI Feedback Toolkit: Sonner only handles toasts. toastory handles toasts, confirmation modals (ui.confirm), prompt inputs (ui.prompt), alerts (ui.alert), multi-step wizards (ui.steps), bottom sheets (ui.bottomSheet), and loading overlays (ui.loading).
2. Apple Frosted Glass System: 21 handcrafted themes with specular highlight borders, deep physical blur, and automatic solid-color fallbacks for older devices.
3. Multi-Framework Portability: Sonner is React-only. toastory runs with the exact same API on React, Next.js, Vue, Nuxt, Angular, Svelte, Electron, and React Native.
4. Zero Runtime Dependencies: Strict zero third-party dependencies in the published bundle.
---
Quick API Comparison
Both libraries share intuitive, chainable toast functions. You can replace your imports without changing your core application code:
// Before (Sonner)toast.success('Event created', { description: 'Monday, 3:00 PM' });
// After (toastory) import { toast, Toaster } from 'toastory/react'; import 'toastory/themes/ios-glass.css';
toast.success('Event created', { description: 'Monday, 3:00 PM' });
`
---
Comprehensive Option Mapping Table
Every Sonner option maps cleanly to toastory:
| Sonner Option | toastory Equivalent | Notes & Enhancements |
|---|---|---|
toast(msg, opts) | toast(msg, opts) | Drop-in identical signature. |
toast.success(msg, opts) | toast.success(msg, opts) | Displays green success badge with WCAG AA contrast. |
toast.error(msg, opts) | toast.error(msg, opts) | Displays red error badge; announces assertively. |
toast.info(msg, opts) | toast.info(msg, opts) | Displays neutral blue informational card. |
toast.warning(msg, opts) | toast.warning(msg, opts) | Displays amber caution pill. |
toast.loading(msg, opts) | toast.loading(msg, opts) | Displays animated glass spinner. |
toast.promise(promise, msgs) | toast.promise(promise, msgs) | Identical syntax, plus returns .unwrap() to await the result. |
toast.custom(fn, opts) | toast.custom(fn, opts) | Render any custom JSX / HTML. |
toast.dismiss(id?) | toast.dismiss(id?) | Dismisses specific toast or all toasts if no ID passed. |
description | description | Secondary subtitle text. |
duration | duration | Auto-close timeout in ms (set to 0 for persistent). |
action | action: { label, onClick } | Interactive action button. |
cancel | cancel: { label, onClick } | Secondary cancel button. |
closeButton | closeButton | Adds accessible close button with hover state. |
richColors | richColors | Injects semantic background tint mixed with solid surface. |
position | position | 6 screen placements (top-right, top-center, top-left, bottom-right, bottom-center, bottom-left). |
expand | stack: 'expanded' | Expands notifications into a list instead of stacked deck. |
theme="system" | mode: 'auto' | Supports 'light', 'dark', and 'auto'. |
---
Side-by-Side Code Examples
#### 1. Setup in Next.js App Router
// Before (Sonner: app/layout.tsx)export default function RootLayout({ children }: { children: React.ReactNode }) { return (
`// After (toastory: app/layout.tsx)
import { Toaster } from 'toastory/react';export default function RootLayout({ children }: { children: React.ReactNode }) { return (
`---
#### 2. Async Action with Promise Feedback
// Before (Sonner)
toast.promise(saveUserData(data), {
loading: 'Saving profile...',
success: (res) => `Saved profile for ${res.name}`,
error: 'Could not save profile.',// After (toastory)
const handle = toast.promise(saveUserData(data), {
loading: 'Saving profile...',
success: (res) => Saved profile for ${res.name},
error: (err) => Failed: ${(err as Error).message},
description: 'Syncing encrypted attributes to edge nodes.',
});
// Optional: you can unwrap the original promise result
const result = await handle.unwrap();
`
---
Unlocking Modal Dialogs, Prompts, and Bottom Sheets
When migrating from Sonner, you no longer need separate modal libraries like SweetAlert2 or headless dialog primitives. toastory provides built-in dialog controllers with keyboard focus trapping, accessible live regions, and escape handling:
// Confirmation Modal const confirmRes = await ui.confirm({ title: 'Delete Repository?', text: 'This will permanently destroy all issues and commits.', tone: 'danger', confirmButton: { label: 'Yes, Delete' }, }); if (confirmRes.isConfirmed) { toast.success('Repository deleted'); }
// User Input Prompt
const promptRes = await ui.prompt({
title: 'Workspace Name',
placeholder: 'e.g. acme-corp',
validate: (val) => (!val ? 'Name is required' : undefined),
});
if (promptRes.isConfirmed) {
toast.info(Workspace ${promptRes.value} created);
}
`
Want to test these options live?
Launch the interactive playground to experiment with themes, positions, and live generators.