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-web-component

v0.1.4

Published

An opinionated toast web component, a port of @emilkowalski's Sonner.

Downloads

527

Readme

Sonner Web Component

A web component port of Sonner. No framework required — use it with Django, Rails, Laravel, htmx, Alpine.js, or plain HTML.

Installation

Add the script tag to your page:

<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"></script>

Or install via npm with your preferred package manager:

npm install sonner-web-component

Getting Started

Add <sonner-toaster> to your page.

<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"></script>

<sonner-toaster></sonner-toaster>

Render a toast.

<script type="module">
  import { toast } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"
  toast("Hello!")
</script>

Usage

There are three ways to create toasts: server-rendered HTML, declarative data-toast-* attributes, and the JavaScript toast() API. window.toast is set by default, or you can import { toast } from the module.

Toaster

<sonner-toaster> is the custom element that manages and renders all toasts. Place it once on your page — everything else builds on it. Configure it with attributes.

<sonner-toaster position="top-center" theme="dark" rich-colors close-button></sonner-toaster>

Or from JavaScript:

toast.configure({ duration: 5000, position: "top-center" })

Each toaster renders into its own Shadow DOM. Styles are fully encapsulated — they won't leak into your page and your page's styles won't affect toasts.

Multiple toasters

You can place multiple <sonner-toaster> elements on a page, each with its own configuration. The global toast() API targets the first one — to reach others, call .add() on the element directly.

<sonner-toaster id="alerts" position="top-center" theme="dark"></sonner-toaster>
<sonner-toaster id="notifications" position="bottom-right"></sonner-toaster>

<script type="module">
  document.getElementById("alerts").add("error", "Something went wrong.")
</script>

API reference

| Attribute | Type | Default | Description | |---|---|---|---| | position | string | bottom-right | top-left, top-center, top-right, bottom-left, bottom-center, bottom-right | | theme | string | system | light, dark, system | | rich-colors | boolean | false | Colored backgrounds based on toast type | | expand | boolean | false | Expand toasts by default | | close-button | boolean | false | Show close button on toasts | | invert | boolean | false | Invert default colors | | duration | number | 4000 | Auto-dismiss duration in ms | | gap | number | 14 | Gap between toasts in px | | visible-toasts | number | 3 | Max visible toasts in stack | | offset | string \| number \| object | 24px | Distance from viewport edge | | mobile-offset | string \| number \| object | 16px | Distance from viewport edge on mobile | | dir | string | ltr | Text direction (ltr, rtl, auto) | | window | boolean | true | Set window.toast globally. Use window="false" to opt out | | flush-delay | number | 200 | Delay in ms before consuming <sonner-toast> children after page load | | burst-window | number | 500 | Time window in ms — toasts arriving within this interval auto-expand the stack | | burst-linger | number | 3000 | How long in ms the stack stays expanded after a burst before collapsing |

The element also exposes methods for creating and managing toasts directly. Use these to target a specific toaster when you have more than one.

| Method | Description | |---|---| | .add(level, message, options?) | Create a toast. level is "success", "error", "info", "warning", "loading", or "" | | .dismiss(id?) | Dismiss one toast by ID, or all | | .promise(promise, data) | Create a promise toast | | .configure(opts) | Update configuration | | .reset() | Clear all toasts | | .getToasts() | Get all active toasts |

Server-rendered

Nest <sonner-toast> elements inside <sonner-toaster>. They display as toasts on page load. Works with any templating language.

<sonner-toaster>
  <sonner-toast level="success">Item saved.</sonner-toast>
  <sonner-toast level="error">Something went wrong.</sonner-toast>
</sonner-toaster>

Declarative Triggers

Elements with data-toast-* attributes trigger toasts on click.

<button data-toast-success="Saved!">Save</button>
<button data-toast-error="Failed." data-toast-description="Please try again.">Delete</button>

Toast JavaScript API

You can call it with just a string, or provide an options object as the second argument.

toast("Hello!")
toast("Hello!", { description: "This is a description.", duration: 5000 })

Position

Override the toaster's default position on a per-toast basis. The toaster creates a separate group for each position.

toast.success("Saved!", { position: "top-center" })
toast.error("Failed.", { position: "bottom-right" })

Types

Each type renders a corresponding icon.

toast.success("Saved!")
toast.error("Something went wrong.")
toast.info("FYI.")
toast.warning("Careful.")
toast.loading("Working...")

Action and cancel buttons

Action renders a primary button, cancel renders a secondary button. Cancel always closes the toast. Action closes the toast unless you call event.preventDefault() in the onClick callback.

toast("File deleted", {
  action: { label: "Undo", onClick: () => restore() },
  cancel: { label: "Dismiss", onClick: () => {} },
})

Promise

Starts in a loading state and updates automatically after the promise resolves or fails. You can pass a function to the success/error messages to use the result.

toast.promise(fetch("/api/save"), {
  loading: "Saving...",
  success: "Saved!",
  error: "Failed to save.",
})

Dismiss

Each toast returns an ID you can use to dismiss it. Call without an ID to dismiss all toasts.

const id = toast.success("Saved!")
toast.dismiss(id)  // dismiss one
toast.dismiss()    // dismiss all

Reset

Clears all toasts but keeps the toaster on the page.

toast.reset()

Destroy

Removes the toaster element from the DOM entirely. A new one is created automatically on the next toast() call.

toast.destroy()

Get toasts

Returns all active toasts.

toast.getToasts()

API reference

| Option | Type | Default | Description | |---|---|---|---| | description | string | - | Secondary text below the title | | duration | number | 4000 | Auto-dismiss time in ms (Infinity to persist) | | dismissible | boolean | true | Whether the toast can be swiped/dismissed | | richColors | boolean | toaster default | Colored backgrounds based on toast type | | closeButton | boolean | toaster default | Show close button on this toast | | invert | boolean | toaster default | Invert default colors for this toast | | position | string | bottom-right | Override position for this toast | | id | number | - | Reuse an ID to update an existing toast | | html | string | - | Raw HTML content (trusted content only) | | action | { label, onClick } | - | Action button | | cancel | { label, onClick } | - | Cancel button | | onDismiss | function | - | Called when dismissed by user | | onAutoClose | function | - | Called when dismissed by timer |

Bridges

Bridges connect Sonner Web Component to other libraries. Each bridge is a side-effect import — add the script tag and it wires itself up. No configuration needed.

htmx

The htmx bridge listens for toast events on document, which is how htmx dispatches events from the HX-Trigger response header (they bubble up from document.body). Set the header on any htmx response and toasts appear automatically.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/htmx.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bridges/htmx.js"></script>

<sonner-toaster></sonner-toaster>

<button hx-post="/save" hx-swap="none">Save</button>

Your server sets the HX-Trigger response header with a toast event:

HTTP/1.1 200 OK
HX-Trigger: {"toast": {"level": "success", "message": "Saved!"}}

Multiple toasts can be sent at once:

HTTP/1.1 200 OK
HX-Trigger: {"toast": [{"level": "success", "message": "Saved!"}, {"level": "info", "message": "Notified team."}]}

fetch

The fetch bridge lets your server trigger toasts from any fetch call — form submissions, API requests, or anything else that uses fetch. It also works as a way to connect an SPA to a server-rendered page that hosts the toaster. It monkey-patches window.fetch to read the X-Toasts header from every response. If the header is present, the toasts are rendered automatically.

<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bridges/fetch.js"></script>

<sonner-toaster></sonner-toaster>

<script type="module">
  // toasts from the response header appear automatically
  const response = await fetch("/api/save", { method: "POST" })
</script>

Your server includes the X-Toasts response header:

HTTP/1.1 200 OK
X-Toasts: [{"level": "success", "message": "Saved!"}]

The level field accepts "success", "info", "warning", or "error". The response body is unaffected — the bridge only reads the header.

Alpine.js

The Alpine.js bridge registers a $toast magic that exposes the full toast API inside Alpine expressions.

<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bridges/alpine.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

<sonner-toaster></sonner-toaster>

<div x-data>
  <button @click="$toast.success('Saved!')">Save</button>
  <button @click="$toast.error('Failed.')">Delete</button>
  <button @click="$toast('Hello!', { description: 'A plain toast.' })">Toast</button>
</div>

The $toast magic supports all the same methods: $toast.success(), $toast.error(), $toast.info(), $toast.warning(), $toast.loading(), $toast.promise(), and $toast.dismiss().

License

Sonner Web Component is licensed under the MIT license. See the LICENSE file for more information.

Sonner is licensed under the MIT license and is Copyright (c) 2023 Emil Kowalski.