Docs Menu
Docs/Migration/Migrating from Sonner to toastory
Updated October 2026

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:

typescript
// 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 Optiontoastory EquivalentNotes & 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.
descriptiondescriptionSecondary subtitle text.
durationdurationAuto-close timeout in ms (set to 0 for persistent).
actionaction: { label, onClick }Interactive action button.
cancelcancel: { label, onClick }Secondary cancel button.
closeButtoncloseButtonAdds accessible close button with hover state.
richColorsrichColorsInjects semantic background tint mixed with solid surface.
positionposition6 screen placements (top-right, top-center, top-left, bottom-right, bottom-center, bottom-left).
expandstack: '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

tsx
// Before (Sonner: app/layout.tsx)

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } `

tsx
// After (toastory: app/layout.tsx)
import { Toaster } from 'toastory/react';

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } `

---

#### 2. Async Action with Promise Feedback

typescript
// 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:

typescript

// 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.

Open in Playground →