mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmWeb Frontendupdated 08 Aug 2026

react-toastify

React-Toastify is a React 18 and 19 notification system built around one mounted `ToastContainer` and an imperative `toast` API. It covers timed success and error messages, promise progress, manual updates, queues, stacked layouts, swipe dismissal, right-to-left layouts, custom React content, controlled progress, keyboard focus, and lifecycle events. Version 11.1 injects its default CSS when the styled container mounts; an unstyled entry is available for teams that want to own every visual rule.

Verdict

A mature choice when a React app needs more than a one-line success message, especially promise updates, queues, custom content, or stacked notifications. Prefer a smaller primitive when the app has only a few local messages, and audit roles, timers, and global state before treating the defaults as finished UX.

API stability4/5The central `ToastContainer` plus `toast()` model has lasted across many releases, and version 11.1 retains typed helpers for success, error, promise, loading, update, dismiss, progress, pause, and change events. Major upgrades still require attention: the v11 migration guide removed public hooks, SCSS and minimal CSS outputs, several styling props, and children arguments from lifecycle callbacks, while changing the stylesheet setup.
Docs4/5The documentation site has focused pages for installation, positioning, styling, promise handling, updates, controlled progress, multiple containers, accessibility, animations, and migration from earlier majors. Public TypeScript declarations document defaults for timers, focus loss, dragging, roles, themes, and hotkeys. Some current details are easier to find in source and release notes, particularly the 11.1 CSP nonce and the difference between the styled and unstyled entry points.
Maintenance4/5Version 11.1.0 and its matching repository push both landed on April 19, 2026. That release added CSP nonce support, fixed touch and vertical dragging, corrected change-event order, improved stacked layouts, and added progress-bar ARIA values. GitHub reports 105 open issues and pull requests, so the queue is material, but the release addresses specific reported defects and updates support for current React tooling.
Ecosystem5/5React-Toastify recorded 4,025,337 downloads for July 31 through August 6, 2026, and the repository has 13,440 stars. It supports React 18 and 19, bundles TypeScript declarations, ships CommonJS and ESM entries, offers a separate unstyled build and notification-center add-on, and has enough adoption that framework examples and testing recipes are easy to find. Its only runtime dependency beyond React peers is clsx.

Use it if

  • You need a proven drop-in toast system with promise, update, queue, progress, and dismissal APIs
  • Your React 18 or 19 app wants consistent notifications callable from event handlers and non-component modules
  • You need stacked toasts, swipe-to-dismiss behavior, right-to-left support, themes, or multiple independently configured containers
  • You want typed custom toast components that receive data, pause state, toast options, and a close function
Skip it if

Setup reality

Install `react-toastify` alongside React 18 or 19 and mount one `ToastContainer` near the client-side application root. In version 11.1 the normal entry automatically injects its stylesheet when that container mounts, so old tutorials that import `ReactToastify.css` are no longer required. A strict Content Security Policy needs the request nonce passed to `ToastContainer`; the 11.1 release added that prop specifically for the injected style tag. If the design system owns all styles, import from `react-toastify/unstyled` instead and provide the complete layout, transition, progress, and responsive CSS yourself. Next.js App Router users need a small `use client` wrapper because the container uses hooks and browser events. Calls made before a container mounts are queued, which is convenient in a single app but can hide a missing container in tests. Defaults are not neutral: toasts close after 5,000 milliseconds, pause on hover and window focus loss, do not close on content click, allow touch dragging, appear at the top right, use the light theme, and have role `alert`. The container also registers Alt+T to focus the first notification and Escape to collapse a stack; replace `hotKeys` and the aria label if those choices conflict with your app. A `limit` keeps excess toasts in a waiting queue, so dismissing visible toasts does not empty that queue unless you also call `toast.clearWaitingQueue()`. Mounting several containers requires stable `containerId` values on both the containers and toast calls. In tests, unmount containers and unsubscribe `toast.onChange` listeners between cases to prevent global state from leaking across assertions.

Patterns

Mount one styled toast containermount-toast-container

'use client';

import {ToastContainer, toast} from 'react-toastify';

export function Notifications() {
  return <ToastContainer aria-label="Application notifications" />;
}

export function notifySaved() {
  toast.success('Saved');
}

The v11.1 styled entry injects CSS when the container mounts; Next.js App Router needs the client boundary shown here.

Show typed status variantsshow-toast-variants

toast.info('Sync started');
toast.success('Profile saved', {autoClose: 2500});
toast.warning('Storage is almost full');
toast.error('Upload failed', {autoClose: false});

autoClose defaults to 5,000 milliseconds; false keeps a toast open until user or code dismisses it.

Turn a promise into pending, success, and error statestrack-promise

await toast.promise<User, Error>(saveUser(), {
  pending: 'Saving user...',
  success: {
    render: ({data}) => `Saved ${data.name}`,
  },
  error: {
    render: ({data}) => `Save failed: ${data.message}`,
  },
});

toast.promise returns the original promise, and resolved or rejected data is passed to the matching render callback.

Update a manually controlled loading toastupdate-loading-toast

const id = toast.loading('Uploading...');

try {
  await uploadFile(file);
  toast.update(id, {
    render: 'Upload complete',
    type: 'success',
    isLoading: false,
    autoClose: 3000,
  });
} catch {
  toast.update(id, {
    render: 'Upload failed',
    type: 'error',
    isLoading: false,
    autoClose: false,
  });
}

Reset isLoading and choose a new autoClose value or the updated toast can remain in its loading behavior.

Prevent duplicate notificationsprevent-duplicates

const OFFLINE_TOAST = 'offline';

if (!toast.isActive(OFFLINE_TOAST)) {
  toast.error('You are offline', {
    toastId: OFFLINE_TOAST,
    autoClose: false,
  });
}

A stable toastId is usually enough to deduplicate; isActive is useful when creation depends on current visibility.

Dismiss visible toasts and clear a limited queuedismiss-and-clear

toast.dismiss();
toast.clearWaitingQueue();

// Or target one toast:
toast.dismiss('offline');

dismiss removes displayed toasts, but when a container has a limit the waiting queue must be cleared separately.

Pass typed data to a custom toastrender-custom-content

import type {ToastContentProps} from 'react-toastify';

type Invite = {email: string};

function InviteToast({data, closeToast}: ToastContentProps<Invite>) {
  return (
    <div>
      Invite sent to {data.email}
      <button onClick={() => closeToast('undo')}>Undo</button>
    </div>
  );
}

toast<Invite>(InviteToast, {data: {email: 'ada@example.com'}});

A string passed to closeToast becomes the onClose reason, which lets application code distinguish custom actions.

Configure stacked and limited notificationsconfigure-stacked-limit

<ToastContainer
  position="bottom-right"
  theme="dark"
  stacked
  newestOnTop
  limit={3}
  closeOnClick
  nonce={cspNonce}
  aria-label="Status messages"
/>

The nonce is applied to the injected style tag; excess notifications wait in a queue once the limit is reached.

Route notifications to separate containersroute-to-container

<ToastContainer containerId="global" position="top-right" />
<ToastContainer containerId="editor" position="bottom-center" />

toast('Draft autosaved', {containerId: 'editor'});
toast.error('Session expired', {containerId: 'global'});

Every targeted toast must use a containerId that matches a mounted container or it will not appear where expected.

Drive a toast progress bar manuallycontrol-progress

const id = toast('Uploading...', {
  autoClose: false,
  progress: 0,
});

upload.onProgress((loaded, total) => {
  toast.update(id, {progress: loaded / total});
});

upload.onComplete(() => toast.done(id));

Controlled progress values run from 0 to 1; toast.done advances to 1 and lets the notification complete.

Subscribe to global toast changesobserve-toast-lifecycle

const unsubscribe = toast.onChange((item) => {
  console.log(item.id, item.status, item.reason);
});

// During application cleanup:
unsubscribe();

Status is added, updated, or removed; v11.1 reports removal synchronously when toast.dismiss is called.

Own all styles with the unstyled builduse-unstyled-entry

import {ToastContainer, toast} from 'react-toastify/unstyled';
import './notifications.css';

<ToastContainer toastClassName="app-toast" />;
toast('Custom design system toast');

The unstyled entry never injects the default stylesheet, so your CSS must handle placement, sizing, transitions, progress, and mobile layout.

Alternatives

PackageRegistryPick it when
sonnernpmYou want a more opinionated, compact toast component with a smaller configuration surface
react-hot-toastnpmYou want a headless-friendly React toaster with a concise API and custom render access
notistacknpmYour application already uses Material UI patterns and wants snackbar queues plus provider-based configuration
@radix-ui/react-toastnpmYou want composable unstyled primitives and will build the notification store, styling, and application policy yourself