npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sonner-vanilla

v0.1.2

Published

An opinionated toast component for vanilla JavaScript/TypeScript

Readme

Sonner Vanilla

A vanilla JavaScript port of the Sonner toast library. Zero dependencies.

For Rails users: See the main rails README for Hotwire/Stimulus integration.

Installation

npm install sonner-vanilla

Usage

import { toast, createToaster } from 'sonner-vanilla';
import 'sonner-vanilla/styles.css';

// Initialize the toaster (do this once)
const toaster = createToaster({
  position: 'bottom-right',
  theme: 'light',
  richColors: true,
});
toaster.mount(document.body);

// Show toasts
toast('Hello World');
toast.success('Success!');
toast.error('Something went wrong');
toast.warning('Warning message');
toast.info('Info message');
toast.loading('Loading...');

Toast Types

// Default
toast('Default message');
toast.message('Same as default');

// Success
toast.success('Operation completed!');

// Error
toast.error('Something went wrong');

// Warning
toast.warning('Please review your input');

// Info
toast.info('New updates available');

// Loading
const id = toast.loading('Processing...');
// Later, dismiss or update it
toast.dismiss(id);

Toast Options

toast.success('Saved!', {
  description: 'Your changes have been saved.',
  duration: 5000,           // Duration in ms (Infinity for persistent)
  dismissible: true,        // Can be dismissed by user
  closeButton: true,        // Show close button
  position: 'top-center',   // Override toaster position
  richColors: true,         // Override toaster richColors
  className: 'my-toast',    // Custom class
  id: 'my-toast-id',        // Custom ID (for updating/dismissing)

  // Callbacks
  onDismiss: (t) => console.log('Dismissed', t),
  onAutoClose: (t) => console.log('Auto closed', t),
});

Action Buttons

toast('File deleted', {
  action: {
    label: 'Undo',
    onClick: () => restoreFile(),
  },
});

toast('Confirm action', {
  action: {
    label: 'Confirm',
    onClick: () => doSomething(),
  },
  cancel: {
    label: 'Cancel',
    onClick: () => console.log('Cancelled'),
  },
});

Promise Toast

toast.promise(saveData(), {
  loading: 'Saving...',
  success: 'Saved successfully!',
  error: 'Failed to save',
});

// With dynamic messages
toast.promise(fetchUser(), {
  loading: 'Loading user...',
  success: (user) => `Welcome, ${user.name}!`,
  error: (err) => `Error: ${err.message}`,
});

// Access the promise result
const result = toast.promise(fetchData(), {
  loading: 'Loading...',
  success: 'Done!',
  error: 'Failed',
});

result.unwrap().then(data => {
  console.log('Data:', data);
});

Custom Content

Render completely custom toast content:

// HTML string
toast.custom('<div class="my-toast"><strong>Bold</strong> message</div>');

// HTMLElement
const el = document.createElement('div');
el.className = 'custom-toast';
el.innerHTML = '<span>Custom element</span>';
toast.custom(el);

// Builder function (receives toast ID)
toast.custom((id) => {
  const div = document.createElement('div');
  div.innerHTML = `
    <p>Custom toast #${id}</p>
    <button onclick="toast.dismiss('${id}')">Close</button>
  `;
  return div;
});

// With options
toast.custom('<div>Custom</div>', {
  duration: 10000,
  position: 'top-center',
});

Dismissing Toasts

// Dismiss by ID
const id = toast.loading('Loading...');
toast.dismiss(id);

// Dismiss all
toast.dismiss();

Updating Toasts

const id = toast.loading('Uploading...', { id: 'upload' });

// Update the same toast
toast.success('Upload complete!', { id: 'upload' });

Toaster Options

const toaster = createToaster({
  position: 'bottom-right',     // Toast position
  theme: 'light',               // 'light', 'dark', 'system'
  richColors: false,            // Colorful backgrounds
  closeButton: false,           // Show close button
  expand: false,                // Expand toasts by default
  duration: 4000,               // Default duration (ms)
  visibleToasts: 3,             // Max visible toasts
  gap: 14,                      // Gap between toasts (px)
  offset: '24px',               // Offset from viewport
  dir: 'auto',                  // 'ltr', 'rtl', 'auto'
  hotkey: ['altKey', 'KeyT'],   // Keyboard shortcut to focus
  containerAriaLabel: 'Notifications',

  // Default options for all toasts
  toastOptions: {
    className: '',
    duration: 4000,
    closeButton: false,
  },
});

Lifecycle

// Mount to a specific element
const toaster = createToaster();
toaster.mount(document.getElementById('toaster-container'));

// Unmount when done
toaster.unmount();

Accessing Toasts

// Get all toasts (including dismissed)
const history = toast.getHistory();

// Get active toasts only
const active = toast.getToasts();

Styling

The default styles are included in sonner-vanilla/styles.css. You can customize using CSS variables or override the classes.

/* Custom theme example */
[data-sonner-toaster][data-theme="dark"] {
  --normal-bg: #1a1a1a;
  --normal-text: #fff;
}

/* Custom toast styles */
[data-sonner-toast][data-type="success"] {
  --success-bg: #10b981;
  --success-text: #fff;
}

TypeScript

Full TypeScript support is included:

import {
  toast,
  createToaster,
  type ToastT,
  type ToasterOptions,
  type ExternalToast,
  type Position,
  type Theme,
} from 'sonner-vanilla';

License

MIT