Sonner-Style Toasts in Vanilla JavaScript – vanilla-sonner

Category: Javascript , Notification | October 14, 2026
Authordevwaseem
Last UpdateOctober 14, 2026
LicenseMIT
Views0 views
Sonner-Style Toasts in Vanilla JavaScript – vanilla-sonner

vanilla-sonner is a framework-free JavaScript library for Sonner-style toast notifications.

It keeps loading messages visible as tasks progress, updates their status in place, and lets your app set capacity and overflow rules for each stack.

Features

  • Success, info, warning, error, and loading notifications.
  • In-place toast updates and animated or immediate dismissal.
  • Promise-based messages and controlled upload progress.
  • Per-position capacity limits and configurable overflow policies.
  • Action buttons, swipe dismissal, and keyboard navigation.
  • Custom HTML templates for trusted content.
  • Independent toaster instances and message deduplication.
  • Light, dark, and system themes, rich colors, and CSS styling hooks.

How To Use It

Basic Usage

Load the stylesheet and UMD script, then place <ol id="sonner-toast-container"> in the page before calling toast().

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Toast Notification Example</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanilla-sonner.min.css">
</head>
<body>
  <button id="save-settings" type="button">Save settings</button>
  <ol id="sonner-toast-container" position="bottom-right" duration="4000"></ol>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanilla-sonner.umd.min.js"></script>
  <script>
    document.querySelector('#save-settings').addEventListener('click', () => {
      toast.success('Settings saved.');
    });
  </script>
</body>
</html>

Install with npm

For a bundler-based project, install the package and import its stylesheet.

npm install vanilla-sonner
import { toast } from 'vanilla-sonner';
import 'vanilla-sonner/style.css';
toast.success('Changes saved.');
toast.message('Update available', 'Restart to apply the new version.');

Display Status Messages

The standard helpers accept a message and an optional per-toast configuration object. toast.message() takes a title, a description, and an optional configuration object.

toast('Message saved.');
toast.info('Sync started.');
toast.success('Export complete.');
toast.warning('Storage is almost full.');
toast.error('Could not save your changes.');
toast.message('Update ready', 'Restart the application to install it.');

Update a Loading Toast

Use the ID returned by toast.loading() to update progress and finish the notification in place. Progress accepts a number from 0 to 1. The example calls an application-defined uploadFile(callback) function that returns a promise and reports uploaded bytes and total bytes.

async function uploadReport(uploadFile) {
  const uploadId = toast.loading('Uploading report...', { progress: 0 });
  try {
    await uploadFile((loaded, total) => {
      if (total <= 0) return;
      const progress = loaded / total;
      toast.update(uploadId, {
        progress,
        message: `Uploading ${Math.round(progress * 100)}%`
      });
    });
    toast.done(uploadId, {
      message: 'Report uploaded.',
      duration: 3000
    });
  } catch {
    toast.update(uploadId, {
      type: 'error',
      message: 'Upload failed.',
      duration: 6000
    });
  }
}

Show Toasts for Promises

toast.promise() displays loading feedback and updates the notification when an existing promise settles. It returns the original promise, which can reject and should be handled by the caller. A short delay avoids flashing a loading message for fast requests. Results from previously dismissed or manually updated toasts are ignored. Since fetch() resolves for HTTP error responses, check response.ok to display an error toast for unsuccessful requests.

async function loadProfile() {
  const request = fetch('/api/profile').then(response => {
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response;
  });
  return toast.promise(request, {
    loading: 'Loading profile...',
    success: () => 'Profile loaded.',
    error: (error) => ({
      message: 'Could not load profile.',
      description: error instanceof Error ? error.message : 'Unknown error',
      duration: 8000
    })
  }, { delay: 250 });
}

Create a Custom HTML Toast

Standard toast messages, descriptions, and action labels render as text. For HTML markup, define a <template> and pass its ID to toast.custom(). Placeholders use simple {{ key }} tokens, and {{ id }} contains the current toast element ID.

<template id="connection-toast">
  <div>
    <strong>Connected to {{ network }}</strong>
  </div>
</template>
toast.custom('connection-toast', { network: 'Office Wi-Fi' }, {
  duration: 4000
});

Add an Action Button

An action can run a synchronous or asynchronous callback. The toast closes after the action completes unless the handler returns false. This example expects restoreArchivedMessage() to be defined in the application.

toast('Message archived.', {
  duration: 8000,
  action: {
    label: 'Undo',
    onClick: async () => {
      await restoreArchivedMessage();
    }
  }
});

Configure Separate Notification Stacks

Use createToaster() for a notification area with independent settings. It accepts a CSS selector or an HTMLElement as its first argument and an optional ToasterConfig object as its second. A unique id also lets the global API find that instance. Call destroy() when removing the instance’s application area.

<ol id="critical-alerts"></ol>
import { createToaster, toast } from 'vanilla-sonner';
const alerts = createToaster('#critical-alerts', {
  id: 'alerts',
  position: 'top-center',
  defaults: {
    error: { important: true, duration: 0 }
  }
});
alerts.error('Connection lost.');
toast.for('alerts').warning('Trying to reconnect...');
toast.info('Sent to alerts.', { toasterId: 'alerts' });

Configure Toast Stacking and Timing

Set global defaults with toast.configure(), either before the first toast or while the page is running. Each effective screen position has its own maxToasts capacity. The queue policy displays the oldest pending notifications first and pauses the timers of those waiting.

toast.configure({
  maxToasts: 3,
  overflow: 'queue',
  position: 'bottom-end',
  theme: 'system',
  hotkey: 'Alt+T',
  defaults: {
    default: { duration: 4000 },
    success: { duration: 2500 },
    error: { duration: 8000, important: true },
    loading: { duration: 0 }
  }
});

Complete Configuration Reference

Per-Toast Options (`ToastConfig`)

OptionType, Default, and Behavior
idstring. Generated when absent. Reuses an active toast ID for in-place updates.
toasterIdstring. Defaults to the default toaster. Selects a registered toaster for global creation helpers.
durationnumber. Defaults to toaster configuration. Auto-dismiss time in milliseconds. 0 disables the timer.
delaynumber. Default 0. Wait time in milliseconds before mounting.
actionToastAction. Optional button with label and onClick(id) handler.
closeButtonboolean. Defaults to toaster configuration. Displays a close button when dismissal is permitted.
theme'light' | 'dark' | 'system'. Defaults to toaster configuration. Overrides the theme of this toast.
positionToastPosition. Defaults to toaster configuration. Sets physical or logical placement.
richColorsboolean. Defaults to toaster configuration. Uses notification-type colors.
importantboolean. Default false. Uses role="alert" and aria-live="assertive".
progressnumber. Optional value from 0 to 1. Out-of-range values are clamped.
dismissibleboolean. Default true. Controls user-initiated dismissal.
closeOnClickboolean. Default false. Dismisses on a click outside interactive content.
pauseOnHoverboolean. Default true. Pauses timing on stack hover.
swipeDirectionsToastSwipeDirection[]. Position-aware defaults. Each entry is 'left', 'right', 'up', or 'down'.
swipeThresholdnumber. Default 0.35. Required swipe distance as a fraction of width or height.
unstyledboolean. Defaults to toaster configuration. Removes built-in toast surface decoration.
dedupeboolean | 'message'. Defaults to toaster configuration. Controls duplicate type/message filtering.
dataRecord<string, unknown>. Optional metadata copied into toast snapshots.
onClose(id, reason) => void. Optional callback when dismissal begins.
onRemove(id, reason) => void. Optional callback after DOM removal.

Toaster Options (`ToasterConfig`)

Pass these options to createToaster() or use toast.configure() for the default toaster.

OptionDefault and Behavior
idContainer ID. Registers the toaster for toast.for(id) and the toasterId option.
maxToastsContainer max-toasts or 3. Maximum visible notifications for each position.
overflow'hide'. Accepts 'hide', 'queue', 'discard-oldest', and 'discard-newest'.
positionContainer position or 'bottom-right'. Default placement for notifications.
durationContainer duration or 0. Default auto-dismiss time in milliseconds.
closeButtonContainer close-button or false. Shows close controls.
themeContainer theme or light appearance. Accepts 'light', 'dark', and 'system'.
richColorsContainer rich-colors or false. Sets type-specific colors.
expandedContainer expanded or false. Starts with expanded stacks.
stack'collapse'. Accepts 'expand' or 'collapse'. Collapsed stacks expand on hover or focus.
newestOnToptrue. Places newer toasts at the visual front.
densefalse. Uses tighter padding and gaps.
unstyledfalse. Removes default toast surface decoration.
hotkey'Alt+T'. Custom keyboard shortcut or false to disable it.
ariaLabel'Notifications'. Accessible label for the toaster region.
historyLimit100. Maximum stored closed-toast snapshots. 0 disables history.
dedupefalse. Accepts 'message' or (next, active) => existingId | false.
defaultsNo explicit defaults. ToastConfig values for default and individual notification types.
beforeCreateNo hook by default. (next, active) => ToastOptions | false can change or reject creation.

Overflow Policies

ValueBehavior
hideKeeps the newest toasts visible. Hidden notifications retain their running timers.
queueDisplays the oldest waiting capacity first. Timers are paused until queued items become visible.
discard-oldestRemoves the oldest toast immediately once capacity is exceeded.
discard-newestRejects a newly created toast when capacity is full.

HTML Container Attributes

The default container uses <ol id="sonner-toast-container">. Configure its basic behavior through these HTML attributes. Queue policies, hooks, and other advanced settings require the JavaScript API.

AttributeValues and Default
positionPhysical positions. Default bottom-right.
max-toastsPositive integer. Default 3.
durationMilliseconds. Default 0 (persistent).
close-buttontrue or false. Default false.
rich-colorstrue or false. Default false.
themelight, dark, or system. Default light appearance.
expandedtrue or false. Default false.

Public JavaScript API

Toast Creation Methods

APIReturn and Purpose
toast(message, config?)string. Creates a plain text notification.
toast.message(title, description, config?)string. Displays a title and secondary description.
toast.info(message, config?)string. Shows an informational notification.
toast.success(message, config?)string. Shows a success notification.
toast.warning(message, config?)string. Shows a warning notification.
toast.error(message, config?)string. Shows an error notification.
toast.loading(message, config?)string. Shows a loading indicator with persistent timing by default.
toast.promise(promise, messages, config?)Original Promise<T>. Manages loading, success, and error states.
toast.custom(templateId, data, config?)string. Renders a template with Record<string, string> placeholder values.
createToaster(target, config?)ToastAPI. Creates an independent toaster for a CSS selector or HTMLElement.

Creation Parameters

CallParameters
toast(message, config?) and status helpersmessage: string, optional config: ToastConfig.
toast.message(title, description, config?)title: string, description: string, optional config: ToastConfig.
toast.promise(promise, messages, config?)promise: Promise<T>, messages: ToastPromiseMessages<T>, optional config: ToastConfig.
toast.custom(templateId, data, config?)templateId: string, data: Record<string, string>, optional config: ToastConfig.
createToaster(target, config?)`target: string

Lifecycle and Management Methods

APIReturn and Purpose
toast.update(id, options)boolean. Updates the matching active toast in place.
toast.done(id, options?)boolean. Sets progress to 1 and defaults to success. Timing follows the configured defaults, with a 3000 ms fallback.
toast.dismiss(id?)boolean. Animates dismissal of one toast or all active toasts.
toast.remove(id?)boolean. Removes one or all active toasts immediately.
toast.pause(id?)boolean. Pauses one or all toast timers.
toast.resume(id?)boolean. Clears the manual pause on one or all timers.
toast.clearQueue()number. Removes waiting notifications and returns how many were cleared.
toast.isActive(id)boolean. Checks whether the toast is active.
toast.getActive()ToastSnapshot[]. Returns copies of active toast records.
toast.getHistory()ToastSnapshot[]. Returns closed-toast records.
toast.configure(config)void. Updates toaster configuration.
toast.for(toasterId)ToastAPI. Retrieves the registered toaster instance.
toast.destroy()void. Destroys the current toaster instance and releases its resources.

Promise Results and Actions

ContractParameters and Result
ToastPromiseMessages.loadingstring | ToastUpdate. Initial loading content.
ToastPromiseMessages.successstring | ToastUpdate | (value) => string | ToastUpdate. Fulfillment content.
ToastPromiseMessages.errorstring | ToastUpdate | (error) => string | ToastUpdate. Rejection content.
ToastAction.labelstring. Button text.
ToastAction.onClick(id: string) => void | false | Promise<void | false>. Action callback.
toast.custom parameter templateIdstring. ID of an HTML <template> element.
toast.custom parameter dataRecord<string, string>. Template token values.
toast.custom parameter configOptional ToastConfig.

Lifecycle Callbacks and Inspection

Public ItemValues or Description
ToastCloseReasontimeout, close-button, action, programmatic, swipe, click, keyboard, destroy, discarded.
ToastSnapshot.idToast identifier.
ToastSnapshot.typeplain, description, success, info, warning, error, loading, promise, or custom.
ToastSnapshot.messageOptional main text.
ToastSnapshot.descriptionOptional secondary text.
ToastSnapshot.positionOptional physical or logical position.
ToastSnapshot.progressOptional controlled progress value.
ToastSnapshot.statedelayed, queued, visible, or closing.
ToastSnapshot.createdAtCreation timestamp.
ToastSnapshot.closedAtOptional closing timestamp.
ToastSnapshot.closeReasonOptional close reason.
ToastSnapshot.dataOptional copied metadata.

Deduplication and Creation Hooks

Configure toaster-wide duplicate filtering through dedupe: 'message', or supply a resolver that receives a proposed toast and active snapshots. Returning an existing toast ID reuses that ID. beforeCreate receives the proposed options and active snapshots. Return updated options to alter the new toast or false to reject it. The per-toast dedupe: false option overrides toaster-level duplicate checking for one call.

toast.configure({
  dedupe(next, active) {
    const previous = active.find(
      item => item.data?.requestId === next.data?.requestId
    );
    return previous?.id ?? false;
  },
  beforeCreate(next) {
    if (next.message === 'internal-only') return false;
    return { ...next, richColors: true };
  }
});

Styling and Customization

The stylesheet exposes stable data-sonner-part selectors for individual toast elements. Use these selectors for overrides.

unstyled: true removes built-in surface decoration but preserves toast positioning, interactions, and accessibility behavior. For custom layouts, load the library stylesheet first and apply your rules afterward.

Styling Parts

SelectorElement
[data-sonner-part="toast"]Toast surface.
[data-sonner-part="content"]Text and custom content area.
[data-sonner-part="close-button"]Close control.
[data-sonner-part="action-button"]Action control.
[data-sonner-part="progress-track"]Progress track.
[data-sonner-part="progress-bar"]Progress fill.

State Attributes

The toast markup also exposes the following state attributes for conditional CSS.

AttributeStyling Context
data-typeToast type.
data-themeActive theme.
data-rich-colorsRich-color mode.
data-stateToast lifecycle state.
data-mountedMount state.
data-hiddenVisibility state.
data-expandedExpanded-stack state.
data-frontPosition at the front of the stack.
data-x-positionHorizontal screen position.
data-y-positionVertical screen position.
data-swipingActive swipe state.

CSS Custom Properties

These CSS custom properties control the sizing and spacing controls of the default toast stylesheet.

PropertyPurpose
--widthToast width.
--paddingPadding inside the toast surface.
--gapSpacing between stacked toasts.
--x-offsetHorizontal distance from the viewport edge.
--y-offsetVertical distance from the viewport edge.

Custom Style Example

Place custom rules after the library stylesheet to change toast width, padding, and stack spacing.

[data-sonner-part="toast"] {
  --width: 280px;
  --padding: 12px;
  --gap: 14px;
}

Accessibility and Keyboard Controls

Normal messages use a polite live region with role="status". An important: true notification uses assertive alert semantics. The default shortcut Alt+T focuses the newest visible notification. Arrow keys move between visible toasts, Escape dismisses the focused toast, and Tab reaches its buttons. Focus, hover, hidden documents, queued messages, and active swipes can pause dismissal timers independently.

toast.error('Payment failed.', { important: true });
toast.configure({ hotkey: 'Ctrl+Shift+N' });
// Use hotkey: false to disable the shortcut.

Alternatives and Related Resources

You Might Be Interested In:


Leave a Reply