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 KDS icons for the success, info, warning, error, and loading toast types.

Preview

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>
  );
}

Angular edition planned for Phase 5h. Star the repo to track progress.

API

<Toaster>

Wraps sonner's Toaster with KDS 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

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.
  • Spinner - inline loading indicator for in-place work.

On this page