
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`)
| Option | Type, Default, and Behavior |
|---|---|
id | string. Generated when absent. Reuses an active toast ID for in-place updates. |
toasterId | string. Defaults to the default toaster. Selects a registered toaster for global creation helpers. |
duration | number. Defaults to toaster configuration. Auto-dismiss time in milliseconds. 0 disables the timer. |
delay | number. Default 0. Wait time in milliseconds before mounting. |
action | ToastAction. Optional button with label and onClick(id) handler. |
closeButton | boolean. 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. |
position | ToastPosition. Defaults to toaster configuration. Sets physical or logical placement. |
richColors | boolean. Defaults to toaster configuration. Uses notification-type colors. |
important | boolean. Default false. Uses role="alert" and aria-live="assertive". |
progress | number. Optional value from 0 to 1. Out-of-range values are clamped. |
dismissible | boolean. Default true. Controls user-initiated dismissal. |
closeOnClick | boolean. Default false. Dismisses on a click outside interactive content. |
pauseOnHover | boolean. Default true. Pauses timing on stack hover. |
swipeDirections | ToastSwipeDirection[]. Position-aware defaults. Each entry is 'left', 'right', 'up', or 'down'. |
swipeThreshold | number. Default 0.35. Required swipe distance as a fraction of width or height. |
unstyled | boolean. Defaults to toaster configuration. Removes built-in toast surface decoration. |
dedupe | boolean | 'message'. Defaults to toaster configuration. Controls duplicate type/message filtering. |
data | Record<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.
| Option | Default and Behavior |
|---|---|
id | Container ID. Registers the toaster for toast.for(id) and the toasterId option. |
maxToasts | Container max-toasts or 3. Maximum visible notifications for each position. |
overflow | 'hide'. Accepts 'hide', 'queue', 'discard-oldest', and 'discard-newest'. |
position | Container position or 'bottom-right'. Default placement for notifications. |
duration | Container duration or 0. Default auto-dismiss time in milliseconds. |
closeButton | Container close-button or false. Shows close controls. |
theme | Container theme or light appearance. Accepts 'light', 'dark', and 'system'. |
richColors | Container rich-colors or false. Sets type-specific colors. |
expanded | Container expanded or false. Starts with expanded stacks. |
stack | 'collapse'. Accepts 'expand' or 'collapse'. Collapsed stacks expand on hover or focus. |
newestOnTop | true. Places newer toasts at the visual front. |
dense | false. Uses tighter padding and gaps. |
unstyled | false. Removes default toast surface decoration. |
hotkey | 'Alt+T'. Custom keyboard shortcut or false to disable it. |
ariaLabel | 'Notifications'. Accessible label for the toaster region. |
historyLimit | 100. Maximum stored closed-toast snapshots. 0 disables history. |
dedupe | false. Accepts 'message' or (next, active) => existingId | false. |
defaults | No explicit defaults. ToastConfig values for default and individual notification types. |
beforeCreate | No hook by default. (next, active) => ToastOptions | false can change or reject creation. |
Overflow Policies
| Value | Behavior |
|---|---|
hide | Keeps the newest toasts visible. Hidden notifications retain their running timers. |
queue | Displays the oldest waiting capacity first. Timers are paused until queued items become visible. |
discard-oldest | Removes the oldest toast immediately once capacity is exceeded. |
discard-newest | Rejects 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.
| Attribute | Values and Default |
|---|---|
position | Physical positions. Default bottom-right. |
max-toasts | Positive integer. Default 3. |
duration | Milliseconds. Default 0 (persistent). |
close-button | true or false. Default false. |
rich-colors | true or false. Default false. |
theme | light, dark, or system. Default light appearance. |
expanded | true or false. Default false. |
Public JavaScript API
Toast Creation Methods
| API | Return 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
| Call | Parameters |
|---|---|
toast(message, config?) and status helpers | message: 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
| API | Return 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
| Contract | Parameters and Result |
|---|---|
ToastPromiseMessages.loading | string | ToastUpdate. Initial loading content. |
ToastPromiseMessages.success | string | ToastUpdate | (value) => string | ToastUpdate. Fulfillment content. |
ToastPromiseMessages.error | string | ToastUpdate | (error) => string | ToastUpdate. Rejection content. |
ToastAction.label | string. Button text. |
ToastAction.onClick | (id: string) => void | false | Promise<void | false>. Action callback. |
toast.custom parameter templateId | string. ID of an HTML <template> element. |
toast.custom parameter data | Record<string, string>. Template token values. |
toast.custom parameter config | Optional ToastConfig. |
Lifecycle Callbacks and Inspection
| Public Item | Values or Description |
|---|---|
ToastCloseReason | timeout, close-button, action, programmatic, swipe, click, keyboard, destroy, discarded. |
ToastSnapshot.id | Toast identifier. |
ToastSnapshot.type | plain, description, success, info, warning, error, loading, promise, or custom. |
ToastSnapshot.message | Optional main text. |
ToastSnapshot.description | Optional secondary text. |
ToastSnapshot.position | Optional physical or logical position. |
ToastSnapshot.progress | Optional controlled progress value. |
ToastSnapshot.state | delayed, queued, visible, or closing. |
ToastSnapshot.createdAt | Creation timestamp. |
ToastSnapshot.closedAt | Optional closing timestamp. |
ToastSnapshot.closeReason | Optional close reason. |
ToastSnapshot.data | Optional 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
| Selector | Element |
|---|---|
[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.
| Attribute | Styling Context |
|---|---|
data-type | Toast type. |
data-theme | Active theme. |
data-rich-colors | Rich-color mode. |
data-state | Toast lifecycle state. |
data-mounted | Mount state. |
data-hidden | Visibility state. |
data-expanded | Expanded-stack state. |
data-front | Position at the front of the stack. |
data-x-position | Horizontal screen position. |
data-y-position | Vertical screen position. |
data-swiping | Active swipe state. |
CSS Custom Properties
These CSS custom properties control the sizing and spacing controls of the default toast stylesheet.
| Property | Purpose |
|---|---|
--width | Toast width. |
--padding | Padding inside the toast surface. |
--gap | Spacing between stacked toasts. |
--x-offset | Horizontal distance from the viewport edge. |
--y-offset | Vertical 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
- Sonner-Style Stacked Toast Notifications in JavaScript – Toastry
- Simple Interactive Snackbar & Toast Notification In Vanilla JavaScript – notifyjs
- Toast-style Web Notifications In Vanilla JavaScript – VanillaToasts
- Vanilla JS Info/Warning/Error/Danger Toast Notification Library – Toasteur.js







