Skip to main content
Gremorie

Sonner

Transient toast notifications built on sonner. Mount the Toaster once at the root and fire toast() from anywhere.

Overview

Sonner is the transient feedback primitive: "Saved", "Invite sent", "Failed - try again". Mount Toaster once at the application root (typically in app/layout.tsx) and fire toast() from anywhere in the tree without an additional provider.

For persistent in-flow messages use Alert; for critical errors that need acknowledgment use AlertDialog. Toasts should be non-blocking and dismissable.

The Gremorie Toaster follows the document's .dark class automatically and ships with Gremorie icons for the success, info, warning, error, and loading toast types.

Preview

'use client';import { Button } from '@gremorie/rx-forms';import { Toaster, toast } from '@gremorie/rx-overlays';export function SonnerPreview() {  return (    <>      <Button        variant="outline"        onClick={() =>          toast('Primitive added', {            description: 'rx-message is now available in your project.',          })        }      >        Show toast      </Button>      <Toaster />    </>  );}

Anatomy

Toaster                    singleton mount that renders the toast region
├─ toast(message, opts)    imperative API fired from anywhere
│  ├─ toast.success        positive confirmation
│  ├─ toast.error          failure the user should know about
│  ├─ toast.warning        risky state, not yet a failure
│  ├─ toast.info           neutral system message
│  ├─ toast.loading        pending operation
│  └─ toast.promise        auto-transitions loading → success/error
└─ action                  optional inline action button on a toast

Installation

bash npx gremorie@latest add rx-sonner
bash pnpm dlx gremorie@latest add rx-sonner
bash yarn dlx gremorie@latest add rx-sonner
bash bunx --bun gremorie@latest add rx-sonner

Usage

Mount the Toaster once at the root of your application:

// app/layout.tsx
import { Toaster } from '@gremorie/rx-overlays';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  );
}

Then fire toasts from any client component:

'use client';
import { Button } from '@gremorie/rx-forms';
import { toast } from '@gremorie/rx-overlays';

export function SaveButton() {
  return (
    <Button
      onClick={() =>
        toast.success('Saved', {
          description: 'Your changes are live.',
        })
      }
    >
      Save
    </Button>
  );
}

The Angular edition of this component ships from source today (see the workbench for the side-by-side); its registry entry is coming next.

API

<Toaster>

Wraps sonner's Toaster with Gremorie tokens and icons. Mount once per app.

PropTypeDefaultDescription
theme"light" | "dark" | "system"auto-detected from .dark classToast theme. Pass explicitly to override the document-class detection.
position"top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center""bottom-right"Where toasts stack.
richColorsbooleanfalseUse color-coded backgrounds for success, error, warning, info.
expandbooleanfalseAlways expand the stack instead of collapsing on hover.
durationnumber4000Default toast lifetime in milliseconds.
closeButtonbooleanfalseRender a close button on every toast.
gapnumber14Vertical gap between stacked toasts.
offsetstring | number32Distance from the viewport edge.
visibleToastsnumber3Max toasts visible at once.

All other sonner ToasterProps are forwarded.

toast(message, options?)

Default toast. Returns a toast id you can pass to toast.dismiss().

OptionTypeDescription
descriptionstring | ReactNodeSecondary line below the message.
action{ label: string; onClick: () => void }Single inline action button.
cancel{ label: string; onClick?: () => void }Secondary cancel/undo button.
durationnumberOverride the default duration.
idstring | numberCustom id for later updates or dismissal.
onDismiss(toast) => voidFired when the toast is dismissed.
onAutoClose(toast) => voidFired when the toast auto-closes.

Typed shortcuts

MethodIconWhen to use
toast.success(message, options)CircleCheckPositive confirmation.
toast.error(message, options)OctagonXFailure that the user should know about.
toast.warning(message, options)TriangleAlertRisky state that is not yet a failure.
toast.info(message, options)InfoNeutral system message.
toast.loading(message, options)Loader2 (animated)Pending operation. Pair with toast.success on resolution.
toast.promise(promise, opts)Loader2 then resolved iconAuto-transitions loading → success/error from a Promise.
toast.custom(jsx, options)-Fully custom toast body.
toast.dismiss(id?)-Dismiss a specific toast or all.

toast.promise signature:

toast.promise(savePost(), {
  loading: 'Saving...',
  success: (data) => `Saved post #${data.id}`,
  error: 'Save failed',
});

Composition

  1. <Toaster> at the app root (app/layout.tsx for Next.js).
  2. toast() fired imperatively from event handlers, async flows, or effects.
  3. Optional action for undo or follow-up navigation.
  4. toast.promise for any awaitable flow that should communicate progress.

Variations

Toast types

Each button fires a different toast: default, success, error, one with an inline action, and a promise that transitions loading to success.

'use client';import { Button } from '@gremorie/rx-forms';import { Toaster, toast } from '@gremorie/rx-overlays';export function SonnerVariantsPreview() {  return (    <>      <div className="flex flex-wrap gap-2">        <Button          variant="outline"          onClick={() =>            toast('Event created', {              description: 'Friday, June 20 at 10:00 AM',            })          }        >          Default        </Button>        <Button          variant="outline"          onClick={() =>            toast.success('Changes saved', {              description: 'Your profile is up to date.',            })          }        >          Success        </Button>        <Button          variant="outline"          onClick={() =>            toast.error('Save failed', {              description: 'Check your connection and try again.',            })          }        >          Error        </Button>        <Button          variant="outline"          onClick={() =>            toast('Item archived', {              description: 'You can restore it within 30 days.',              action: {                label: 'Undo',                onClick: () => toast('Restored'),              },            })          }        >          With action        </Button>        <Button          variant="outline"          onClick={() =>            toast.promise(new Promise((resolve) => setTimeout(resolve, 1500)), {              loading: 'Publishing post...',              success: 'Post is live',              error: 'Publish failed',            })          }        >          Promise        </Button>      </div>      <Toaster />    </>  );}

Success with undo

The canonical "saved with undo" pattern.

toast.success('Item archived', {
  description: 'You can restore it within 30 days.',
  action: {
    label: 'Undo',
    onClick: () => restore(item.id),
  },
});

Promise

Drive loading, success, and error states from a single call.

toast.promise(api.publish(post), {
  loading: 'Publishing post...',
  success: (post) => `${post.title} is live`,
  error: 'Publish failed - retry?',
});

Custom position with rich colors

Top-center stack with semantic backgrounds.

<Toaster position="top-center" richColors duration={5000} />

Persistent error toast

Disable auto-close for failures that require user attention.

toast.error('Connection lost', {
  description: 'Reconnecting...',
  duration: Infinity,
  action: {
    label: 'Retry now',
    onClick: () => reconnect(),
  },
});

Accessibility

  • Live region: toasts announce via aria-live="polite" by default; assistive tech reads them without interrupting.
  • Errors: toast.error uses aria-live="assertive" so failures are read immediately.
  • Dismiss: Esc dismisses the most recent toast when one is focused.
  • Hover and focus: hovering or focusing the stack pauses the auto-dismiss timer.
  • Keyboard: action buttons inside toasts are reachable via Tab once the stack is focused.
  • Theme: the Toaster watches the document .dark class automatically; pass theme explicitly when mounting in a non-themed iframe or sandbox.
  • Touch: swipe-to-dismiss is implemented by sonner; keyboard users can dismiss via Esc.
  • Reduced motion: enter/exit animations honor prefers-reduced-motion.
  • Alert - persistent in-flow message that lives in the layout.
  • Alert Dialog - interruptive confirmation for irreversible actions.
  • Dialog - focused modal for short flows.

On this page