Search

Jump to any page.

Feedback

Toast

Brief, non-blocking confirmation. Mount the Toaster once, call toast() anywhere.

pnpm dlx shadcn@latest add @paragon/sonner
Brings in@paragon/spinnersonnernext-themeslucide-react
registry/paragon/ui/sonner.tsx
"use client"

import { useTheme } from "next-themes"
import { Toaster as Sonner, type ToasterProps } from "sonner"
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon } from "lucide-react"

import { Spinner } from "@/registry/paragon/ui/spinner"

/**
 * Mount once near the root, then call `toast()` from anywhere. Toasts use the
 * card surface and follow the page direction, so they stack on the reading
 * edge in Arabic.
 */
function Toaster({ ...props }: ToasterProps) {
  const { resolvedTheme } = useTheme()

  return (
    <Sonner
      theme={(resolvedTheme as ToasterProps["theme"]) ?? "system"}
      dir="auto"
      gap={10}
      className="toaster group"
      icons={{
        success: <CircleCheckIcon className="size-4 text-oasis dark:text-[#a8cf9f]" />,
        info: <InfoIcon className="size-4 text-fjord dark:text-[#9db7dc]" />,
        warning: <TriangleAlertIcon className="size-4 text-dune dark:text-mangrove" />,
        error: <CircleAlertIcon className="size-4 text-destructive" />,
        loading: <Spinner aria-hidden="true" role={undefined} className="size-4" />,
      }}
      style={
        {
          "--normal-bg": "var(--popover)",
          "--normal-text": "var(--popover-foreground)",
          "--normal-border": "var(--border)",
          "--border-radius": "var(--radius-card)",
          "--width": "380px",
        } as React.CSSProperties
      }
      toastOptions={{
        classNames: {
          toast:
            "font-sans! gap-3! px-4! py-3.5! shadow-[0_1px_2px_color-mix(in_oklab,var(--ink),transparent_92%),0_16px_36px_-12px_color-mix(in_oklab,var(--ink),transparent_78%)]! dark:shadow-[0_16px_36px_-12px_rgb(0_0_0/0.65)]!",
          title: "text-[0.9375rem]! font-medium! tracking-[-0.01em]",
          description: "text-sm! text-muted-foreground!",
          actionButton:
            "h-8! rounded-[calc(var(--radius-button)-4px)]! bg-primary! px-3! text-[0.8125rem]! font-medium! text-primary-foreground!",
          cancelButton:
            "h-8! rounded-[calc(var(--radius-button)-4px)]! bg-secondary! px-3! text-[0.8125rem]! font-medium! text-secondary-foreground!",
        },
      }}
      {...props}
    />
  )
}

export { Toaster }