# Sonner Alternative for React: 4 Toast Libraries Compared

> Need a Sonner alternative? react-hot-toast, React-Toastify and a source-owned React toast compared on API, swipe, accessibility and bundle size in 2026.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Oct 9, 2026
- Category: [Comparisons and alternatives](https://wingo-ui.com/blog/category/comparisons)
- Reading time: 11 min
- Canonical: https://wingo-ui.com/blog/sonner-alternative-react-toast

## TL;DR

The right Sonner alternative depends on what Sonner does not do for you, and if nothing is wrong, keep it: it is by far the most downloaded of the four toast libraries compared here and has a stack, swipe and promise toasts built in. Pick react-hot-toast for the smallest bundle (4.8 kB gzip) and a headless hook, or React-Toastify for progress bars, a queue limit and a notification history. Pick a source-owned toast, such as the free Wingo UI Toast, when you need to change how the stack behaves on phones, for screen readers or in your design system.

You go looking for a Sonner alternative when something about the toast stops fitting: the bundle, a design system that fights its stylesheet, an accessibility review, or action buttons that are hard to hit on a phone. Sonner is still the default React toast for good reasons, so leaving it should fix a specific problem. This comparison puts Sonner next to react-hot-toast, React-Toastify and a source-owned toast on the API, the stack, swipe, screen readers, phones and bundle size, checked in October 2026 against each package's published code and docs.

We build Wingo UI, whose free Toast is the source-owned option here, so read this as a vendor's comparison with sources; it says where each of the others is the better pick. More head to head comparisons live under [Comparisons and alternatives](https://wingo-ui.com/blog/category/comparisons), and the [guide to shadcn alternatives](https://wingo-ui.com/blog/shadcn-alternatives) compares whole component libraries.

## What is the best Sonner alternative for React?

The best Sonner alternative depends on what you are leaving for:

- **A smaller bundle and your own markup:** react-hot-toast. It measured 4.8 kB gzip in our build, half of Sonner, and its `useToaster` hook lets you render every toast yourself.
- **More features built in:** React-Toastify. Progress bars, a queue `limit`, `toast.update`, a stacked mode, RTL and a notification history add-on, with no extra packages.
- **Behavior you can change:** a toast whose source lives in your repository, such as the free Wingo UI [Toast](https://wingo-ui.com/components/toast). It keeps a Sonner-style `toast()` API, and the stack, the timers and the phone layout are code you can edit.
- **No problem to fix:** stay on Sonner. It is the most downloaded React toast notification library of the four by a wide margin, and shadcn/ui installs it for Radix and React Aria projects.

## How do Sonner, react-hot-toast and React-Toastify compare?

The table reads the latest version of each package on npm as of October 9, 2026. Sizes are our own measurement: each library's toaster component and `toast` function bundled with esbuild 0.28, minified, React left external, then gzipped. Sonner and React-Toastify inject their CSS from JavaScript, so their numbers include the styles. Downloads are npm's count for October 1 to 7, 2026.

| | [Sonner](https://github.com/emilkowalski/sonner) | [react-hot-toast](https://react-hot-toast.com/docs) | [React-Toastify](https://fkhadra.github.io/react-toastify/introduction/) | [Wingo UI Toast](https://wingo-ui.com/components/toast) |
| --- | --- | --- | --- | --- |
| Version | 2.0.8, Aug 9, 2026 | 2.6.1, Sep 16, 2026 | 11.1.0, Apr 19, 2026 | Toast 1.0.0 in registry 1.7.0, Oct 8, 2026 |
| License | MIT | MIT | MIT | Free item, [Wingo UI license](https://wingo-ui.com/legal/license) |
| Weekly npm downloads | 50.8 million | 3.7 million | 2.9 million | Not on npm, copied by the CLI |
| Size, min + gzip | 9.6 kB | 4.8 kB | 9.8 kB | 12.2 kB, plus motion if you lack it |
| Default position and time | bottom-right, 4 s | top-center, 2 s for success, 4 s for others | top-right, 5 s | bottom-right, 4 s |
| Stack | Collapsed, fans out on hover, 3 visible | A list | A list, or collapsed with `stacked` | Collapsed, fans out on hover or tap, 3 visible |
| Swipe to dismiss | 45px or a flick | None | Touch only, 80% of the width | Touch and mouse, a quarter of the toast or a flick |
| Keyboard | Alt+T focuses the list, Esc collapses it | None | Alt+T focuses a toast and pauses, Esc resumes | Alt+T focuses the newest toast, Esc dismisses it |
| Screen reader | One polite region for all toasts | Each toast polite `status` | Each toast `role="alert"` | Each toast polite `status`, errors `alert` |
| Phone layout | Full width below 600px | None built in | Full width below 480px, safe areas | Full width below 768px, safe areas |
| Reduced motion | Transitions off | Fades instead of sliding | No rule in the package | Fades instead of sliding |
| Your own markup | `unstyled`, `toast.custom` | `react-hot-toast/headless` | `react-toastify/unstyled` | Edit `components/ui/toast.tsx` |

The Wingo UI size needs a footnote: in most apps it is the heaviest of the four, because it animates with motion. In a shadcn-style project that already ships tailwind-merge, lucide-react and Radix, it adds 57.6 kB, motion included. If that app already uses motion too, it adds only its own 12.2 kB, and with nothing shared it is 69 kB, seven times Sonner. Its styles are Tailwind classes that compile into your app's stylesheet, so they are not in these numbers.

## Sonner vs react hot toast: which one should you use?

Use Sonner if you want a stack, swipe and action buttons without writing them. Use react-hot-toast if you want the smallest package and plan to render toasts your own way. The two look alike at the call site until you need a button:

```tsx
// sonner: action and cancel are options
import { toast } from "sonner";

export function onDelete(restore: () => void) {
  toast("Listing deleted", {
    description: "City bike, Trek FX 2",
    action: { label: "Undo", onClick: restore },
  });
}
```

```tsx
// react-hot-toast: no action option, so the button is your markup
import toast from "react-hot-toast";

export function onDelete(restore: () => void) {
  toast((t) => (
    <span>
      Listing deleted
      <button
        onClick={() => {
          restore();
          toast.dismiss(t.id);
        }}
      >
        Undo
      </button>
    </span>
  ));
}
```

Beyond buttons, react-hot-toast lists toasts instead of stacking them, has no swipe and no keyboard handling, and pauses its timers only on mouse enter and leave, not for keyboard focus or a background tab. Success toasts default to 2 seconds, which is short for an Undo. Its strength is the headless entry: `useToaster` from `react-hot-toast/headless` hands you the toasts plus `startPause`, `endPause` and `calculateOffset`, the right shape if you want full control of the markup and will write the gestures yourself.

Sonner gives you polished defaults: a 356px stack, swipe in the directions its position allows, `toast.promise`, `richColors` and a close button, with timers that pause while you hover, while you swipe and while the tab is in the background. Its look comes from its own stylesheet, so matching a design system means CSS variables and `classNames`, or `unstyled` and your own markup.

## When is React-Toastify the better pick?

Pick React-Toastify when you need features the others leave out: a progress bar on every toast, on by default, and a controlled `progress` value for uploads. `limit` holds extra toasts in a waiting queue, `toast.update(id, options)` changes a toast in place, `stacked` collapses the list, and the `useNotificationCenter` add-on keeps a history of toasts for an inbox. It handles RTL and positions its container with `env(safe-area-inset-*)` on phones.

Its defaults are the loudest of the four: a close button and a progress bar on every toast, 5 seconds on screen, and `role="alert"` on each toast, so screen readers interrupt the user for a "Saved" message unless you pass `role="status"`. We found no `prefers-reduced-motion` rule in the 11.1.0 package, so the default Bounce transition plays for everyone; pass `transition={Slide}` or your own `cssTransition` if that matters to you.

If those defaults or the styling are why you want a React Toastify alternative, Sonner is the closest swap for the call sites (`toast.success`, `toast.error`, `toast.promise`). The main rename is the promise's loading message: React-Toastify calls it `pending` and Sonner calls it `loading`. A history with read state is an inbox, a different job: Wingo UI keeps it in a separate [Notification Center](https://wingo-ui.com/components/notification-center) with a bell and an unread count.

## What does "shadcn toast" mean in 2026?

It depends on the project's base. Since July 2026 new shadcn/ui projects start on Base UI, and for those `npx shadcn@latest add toast` installs a Toast [built on Base UI primitives](https://ui.shadcn.com/docs/changelog/2026-07-toast), called with `toast.add({ title, description })` and `toast.promise`. In Radix and React Aria projects, the old Toast is marked deprecated and the docs point to [Sonner](https://ui.shadcn.com/docs/components/radix/sonner): `npx shadcn@latest add sonner` writes a `components/ui/sonner.tsx` that wraps the package, maps its colors to your `--popover` and `--border` tokens and reads the theme from `next-themes`.

So the shadcn toast in a Radix project is Sonner with your colors: you own a short wrapper, and the stack, timers and swipe stay in `node_modules/sonner`. The Base UI version hands you more of the markup, with behavior from Base UI's `Toast` parts. Per the [Base UI docs](https://base-ui.com/react/components/toast), that means a 5 second default timeout, a limit of 3, swipe down or right, a `priority: "high"` option for urgent announcements, and F6 to move focus into the toast region.

## What changes on a phone?

The phone is where these libraries differ most, on four points:

- **Width.** Sonner spans the screen below 600px with 16px gutters (`mobileOffset`). React-Toastify spans it below 480px with square corners. react-hot-toast has no phone layout: its toasts cap at 350px and sit 16px from the edges.
- **Buttons.** Sonner's default action and cancel buttons are 24px tall, exactly WCAG 2.2's minimum target size, and its close button is 20px, under it. All of them are far from the 44pt Apple recommends for iOS controls; our [guide to minimum touch target size](https://wingo-ui.com/blog/minimum-touch-target-size-react-tailwind) has the numbers.
- **Safe areas.** React-Toastify positions its container with the safe area insets. Sonner and react-hot-toast do not, so with `viewport-fit=cover` a bottom stack can sit on the iPhone home indicator until you add the inset to the offset yourself. [Tailwind safe area insets](https://wingo-ui.com/blog/tailwind-safe-area-insets) shows the pattern.
- **Swipe.** Sonner dismisses after 45px of travel or a quick flick. React-Toastify swipes on touch only by default and wants 80% of the toast's width. react-hot-toast has no swipe.

We built the Wingo UI Toast around that list. Below 768px toasts span the width with 16px gutters, each edge uses `max(env(safe-area-inset-*), 16px)`, and `mobilePosition="top"` moves them clear of a bottom tab bar. Action and close buttons look 32px tall and keep a 44px hit area on touch screens. With no hover on a phone, a tap fans out the stack.

> Live demo (Toast): Hover the stack to fan it out (tap it on a phone), then swipe the front toast toward the edge. Press Failed payment, then Retry: the same toast turns into a loading state and then a success. Try it at [Toast](https://wingo-ui.com/components/toast) and install it with `npx wingo-ui@latest add toast`.

## How do the toasts sound to a screen reader?

All four announce through ARIA live regions; the difference is what gets interrupted. A polite announcement waits until the screen reader finishes speaking. An assertive one, which `role="alert"` implies, cuts in.

- **Sonner** wraps every toast in one `<section aria-live="polite">` labeled "Notifications alt+T". A failed payment is announced as politely as "Copied".
- **react-hot-toast** gives each toast `role="status"` and `aria-live="polite"`, and you can switch a single toast to `alert` through its `ariaProps` option.
- **React-Toastify** gives each toast `role="alert"`, so every toast interrupts unless you change the role.
- **Wingo UI** makes each toast its own polite `status` and switches errors, and any toast sent with `important: true`, to `role="alert"`.

For the keyboard, Sonner's Alt+T focuses the list and Escape collapses it, React-Toastify's focuses a toast and pauses every timer until Escape, and react-hot-toast has none. In the Wingo UI Toast, Alt+T (Option+T on a Mac) focuses the newest toast, Tab walks through its buttons, Escape dismisses the focused toast, and timers pause while keyboard focus is inside, so there is time to reach Undo.

## When should you skip the toast and use an inline alert?

Skip it when the message has to be read or acted on. A declined card, a form that failed to save, or a session about to expire need a message that stays next to the problem. Toasts time out, sit far from the field that caused them, and on a phone they can cover the submit button. Use an inline alert for those and keep toasts for confirmations, progress and quick undos. The free [Alert](https://wingo-ui.com/components/alert) follows the same split for screen readers: danger and warning tones use `role="alert"`, the others `role="status"`.

> Live demo (Alert): A declined card belongs next to the Pay button: press Try again and the alert collapses, then press Pay to see it expand back in. Try it at [Alert](https://wingo-ui.com/components/alert) and install it with `npx wingo-ui@latest add alert`.

## What does a source-owned toast give you?

A source-owned toast puts the whole component in your repository: the store, the stack, the gesture and the markup. You review it like the rest of your code, and nothing changes under you on `npm update`. The cost is that upstream fixes reach you only when you pull them. The Wingo UI Toast is free; install it with the CLI:

```bash
npx wingo-ui@latest add toast
```

That writes `components/ui/toast.tsx` plus the button, the spinner and the hooks and helpers it uses. Mount the Toaster once, next to the app:

```tsx
// app/providers.tsx
"use client";

import type { ReactNode } from "react";
import { Toaster } from "@/components/ui/toast";

export function Providers({ children }: { children: ReactNode }) {
  return (
    <>
      {children}
      <Toaster richColors mobilePosition="top" />
    </>
  );
}
```

Then call `toast` from anywhere, even outside React or before the Toaster mounts:

```tsx
import { toast } from "@/components/ui/toast";

export function onDelete(restore: () => void) {
  toast("Listing deleted", {
    description: "City bike, Trek FX 2",
    action: { label: "Undo", onClick: restore },
  });
}

export async function publishListing(publish: () => Promise<{ price: string }>) {
  // returns the original promise: await gives you the data or throws the error
  return toast.promise(publish, {
    loading: "Publishing the listing…",
    success: (data) => `Published for ${data.price}`,
    error: "Could not publish the listing",
  });
}
```

Beyond those call sites, the file gives you:

- **Updates by id.** Calling `toast.loading("Uploading 3 photos… 2/3", { id: "upload" })` again updates the toast in place. A new type with the same id, such as `toast.success`, starts it over, so the loading toast's `Infinity` duration never sticks to the success.
- **Your tokens.** The surface uses `bg-popover` and your theme's status tones, so dark mode needs no `theme` prop. Visual props such as `radius`, `width` and `richColors` can also be set app-wide through `UIProvider` defaults.
- **Updates that keep your edits.** `npx wingo-ui@latest update` merges a new release into the file you changed, as [the updates docs](https://wingo-ui.com/docs/updates) describe.

The trade-offs, stated plainly. It is younger than the three packages, and Sonner has years of bug reports behind it. It costs more bytes than any of them, even in an app that already uses motion. In a shadcn project created with a Base UI style, install it with the Wingo UI CLI instead of `shadcn add`, because that style rewrites `asChild`, which the button relies on. The Notification Center is part of [Wingo UI Pro](https://wingo-ui.com/pricing); the Toast and the Alert are free.

## How do you move from Sonner to the Wingo UI Toast?

Most call sites only change the import. The API follows Sonner's: `toast`, `toast.success`, `error`, `warning`, `info` and `loading`, `toast.promise`, `toast.custom`, `toast.dismiss`, the `action`, `cancel` and `toasterId` options, and the `position`, `expand`, `visibleToasts`, `duration`, `richColors` and `closeButton` props keep their names and defaults. The differences:

| Sonner | Wingo UI Toast |
| --- | --- |
| `import { toast } from "sonner"` | `import { toast } from "@/components/ui/toast"` |
| `hotkey={["altKey", "KeyT"]}` | `hotkey="alt+t"` |
| `<Toaster id="editor" />` for a second toaster | `<Toaster toasterId="editor" />` |
| `toast.promise(...).unwrap()` for the result | `toast.promise(...)` returns the original promise |
| `theme` prop, or `next-themes` in the shadcn wrapper | None; the surface uses your tokens |
| `event.preventDefault()` in an action keeps the toast open | The same, or `keepOpen: true` on the action |
| `mobileOffset` below 600px | `mobileOffset` below 768px, plus `mobilePosition` |
| `toast.message`, `toast.getHistory` | Not available; `useToasts()` returns the toasts on screen |

If you used the shadcn wrapper, delete `components/ui/sonner.tsx`, point the `Toaster` import at `@/components/ui/toast`, and replace `from "sonner"` in the files that call `toast`. Then drop any `theme` prop from the Toaster and the `.unwrap()` after promise toasts, which TypeScript flags.

## Where should you start?

If Sonner works and nobody has complained, keep it. If the complaint is about phones, screen readers or a design system that will not bend, try a source-owned toast: run `npx wingo-ui@latest add toast`, which is free and needs no account, swap the import on a few call sites, and open the app on your own phone.

## Components in this post

- [Toast](https://wingo-ui.com/components/toast) (Free): Toast notifications: a Toaster mounted once and a toast() you call from anywhere, stacked, swipeable and paused while you read. Install: `npx wingo-ui@latest add toast`
- [Alert](https://wingo-ui.com/components/alert) (Free): An inline message about a page or a section, from a failed payment to a maintenance banner, in three variants and six tones. Install: `npx wingo-ui@latest add alert`

## FAQ

### What is the best Sonner alternative for React?

It depends on why you are leaving. react-hot-toast is the smallest and has a headless hook, React-Toastify has the most built-in features, and a source-owned toast like the free Wingo UI Toast keeps a Sonner-style API while every behavior lives in a file you can edit.

### Is Sonner better than react-hot-toast?

Sonner does more out of the box: a collapsed stack, swipe to dismiss, action and cancel buttons, a promise toast and an Alt+T shortcut. react-hot-toast is about half the size at 4.8 kB gzip in our build and ships a headless useToaster hook, but it has no swipe, no keyboard shortcut and no built-in action buttons.

### Does shadcn/ui still use Sonner for toasts?

It depends on the project's base. In Radix and React Aria projects the old Toast is deprecated and npx shadcn@latest add sonner installs a short wrapper around the sonner package. In Base UI projects, the default for new projects since July 2026, npx shadcn@latest add toast installs a Toast built on Base UI with a toast.add() API.

### Which React toast library is the smallest?

react-hot-toast, at 4.8 kB minified and gzipped in our esbuild measurement in October 2026. Sonner came to 9.6 kB and React-Toastify to 9.8 kB, both including the CSS they inject from JavaScript.

### Are toast notifications accessible to screen readers?

They are announced through ARIA live regions, but the libraries differ in urgency: Sonner announces everything politely, React-Toastify marks every toast role="alert" by default, and react-hot-toast and the Wingo UI Toast let single toasts interrupt. Anything the user must act on belongs in an inline alert, because toasts time out.

---

Source: https://wingo-ui.com/blog/sonner-alternative-react-toast
