Documentation

A succinct pill toast from Pill Toaster: one frosted surface, clear status, a stacked pile that fans open, drag to dissolve into smoke. Install it from the shadcn registry.

Installation

Install with the shadcn CLI:

pnpm dlx shadcn@latest add https://toast.nabinkhair.com.np/r/pill-toaster.json

One self-contained file, pill-toaster.tsx. The CLI pulls the Button prerequisite automatically (registry dependency), plus motion and lucide-react from npm. Status colors use your existing theme (destructive plus the default palette), so no extra CSS setup is needed.

Mount Toasts once in your root layout:

import { Toasts } from "@/components/ui/pill-toaster"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toasts position="top-center" />
      </body>
    </html>
  )
}

Usage

Import the helper and fire a toast. A plain string renders without an icon; pass an object for a status, action, or custom lifetime.

import { toast } from "@/components/ui/pill-toaster"

toast("Copied")
toast({ message: "Event created", state: "success" })
toast({ message: "Something went wrong", state: "error" })

Reuse an id to update a toast in place, how pending becomes success once work finishes:

const id = "creating-event"
toast({ id, message: "Saving…", state: "pending" })
await save()
toast({ id, message: "Saved", state: "success" })

Types

Pick a status per toast with the state field. Actions ride along in the same object, and pending plus a reused id covers async flows.

const id = "event-created"
toast({
  id,
  message: "Event has been created",
  state: "success",
  action: {
    label: "Undo",
    run() {
      dismissToast(id)
    },
  },
})

Position

Control placement with the position prop on Toasts. Drag a pill in any direction to dissolve it.

<Toasts position="bottom-right" />

This moves the live host, then fires a toast so you see it land there. The equivalent in your app is one line. Active: top-center

<Toasts position="top-center" />

Migrate

Switching from another toast library? Copy an agent prompt, paste it into Cursor or any coding agent, and let it rewrite imports, Toasts mounts, and call sites for Pill Toaster.

Each prompt installs from the registry, maps the old API onto toast / Toasts, and calls out gaps (custom JSX bodies, duration → lifetime, promises → pending + same-id update).

API

Toasts is the host. Mount it once, it listens on a global event bus, so a second host would render every toast twice. It takes a single prop:

  • position — where the stack appears (default top-center)

Fire with toast("message") or toast({ message, state, action, lifetime }):

  • state — pending (sticky), success, error, warning, info. No state renders no icon.
  • lifetime — ms until auto-dismiss. Plain toasts live 2.4s, actions 6s.
  • action — { label, run }. The toast dismisses after run unless it re-issues the same id.
  • id — reuse to update a toast in place instead of adding a new one.
  • dismissToast(id) — dismiss one toast programmatically.

Multiple toasts collapse into a peeking pile and fan out when hovered, focused, or pressed (which also pauses timers). Drag a pill past the threshold and it dissolves into smoke. Pure helpers upsertToast, dismissDelay, tick, pileSlot, and fanSlot are exported for testing.