Toast
A floating notification you can swipe away: it follows the finger and only leaves on release.
Installation
$ pnpm dlx shadcn@latest add @ssntl/toastUsage
import { Toast, ToastAction, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/toast"
<ToastProvider swipeDirection="right">
<Toast open={open} onOpenChange={setOpen}>
<ToastTitle>Order archived</ToastTitle>
<ToastDescription>It moved to Archive with its history.</ToastDescription>
<ToastAction altText="Open Archive to restore it">Undo</ToastAction>
</Toast>
<ToastViewport />
</ToastProvider>Rules
What this component expects from you, as it ships in components/ui/toast.md next to the code. The full file adds when to use it, keyboard, the UI foundations it applies and every shared rule.
## Rules for Toast
- **Floating notification.** Elevation-4, above the page and its menus; announced politely, paused while hovered or focused, reachable with F8.
- **Responsive gesture.** The toast follows the finger 1:1 from the first pixel.
- **Continuous gesture.** The pointer stays captured even when it leaves the toast.
- **Cancelable gesture.** It dismisses on release past the threshold; letting go short of it springs back.
- One action at most, with `altText` for people who can't reach the toast in time.
- With an action or an error it stays until dismissed (duration={Infinity}); a plain confirmation lasts at least 5 seconds.
- The close button reads “Dismiss notification” by default; change `closeLabel` in other languages.Code
"use client"
import * as React from "react"
import { Toast as ToastPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { CloseButton } from "@/registry/ssntl/ui/close-button"
// A floating notification (Radix Toast): announced politely, paused while
// hovered or focused, and reachable with F8. Floating notification:
// elevation 4, above the page and its menus.
// Gestures: swipe right to dismiss. The toast follows the finger 1:1 from
// the first pixel (responsive), keeps the pointer even off the toast
// (continuous) and only dismisses on release past the threshold; letting go
// short of it springs back (cancelable).
const ToastProvider = ToastPrimitive.Provider
function ToastViewport({
className,
...props
}: React.ComponentProps<typeof ToastPrimitive.Viewport>) {
return (
<ToastPrimitive.Viewport
data-slot="toast-viewport"
className={cn(
"fixed inset-x-4 bottom-4 z-50 mx-auto grid max-w-sm gap-2 outline-none",
className
)}
{...props}
/>
)
}
function Toast({
className,
children,
closeLabel = "Dismiss notification",
...props
}: React.ComponentProps<typeof ToastPrimitive.Root> & {
/** The close button's accessible name. */
closeLabel?: string
}) {
return (
<ToastPrimitive.Root
data-slot="toast"
className={cn(
"relative grid touch-pan-y gap-1 rounded-lg bg-popover p-5 pe-16 text-left text-popover-foreground elevation-4 select-none",
"data-[state=closed]:pop-out data-[state=open]:pop-in",
"data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[swipe=move]:transition-none",
"data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-[translate] data-[swipe=cancel]:duration-(--duration-menu) data-[swipe=cancel]:ease-out",
"data-[swipe=end]:translate-x-(--radix-toast-swipe-end-x) data-[swipe=end]:pop-out",
className
)}
{...props}
>
{children}
<ToastPrimitive.Close asChild>
<CloseButton label={closeLabel} className="absolute end-2 top-2" />
</ToastPrimitive.Close>
</ToastPrimitive.Root>
)
}
function ToastTitle({
className,
...props
}: React.ComponentProps<typeof ToastPrimitive.Title>) {
return (
<ToastPrimitive.Title
data-slot="toast-title"
className={cn("text-label", className)}
{...props}
/>
)
}
function ToastDescription({
className,
...props
}: React.ComponentProps<typeof ToastPrimitive.Description>) {
return (
<ToastPrimitive.Description
data-slot="toast-description"
className={cn(
"text-caption text-pretty text-muted-foreground",
className
)}
{...props}
/>
)
}
/** One follow-up action, such as Undo; altText says how to do it otherwise. */
function ToastAction({
className,
...props
}: React.ComponentProps<typeof ToastPrimitive.Action>) {
return (
<ToastPrimitive.Action
data-slot="toast-action"
className={cn(
"mt-2 inline-flex h-9 w-fit cursor-pointer items-center rounded-full bg-secondary px-4 text-caption font-medium",
"transition-[background-color,scale] duration-(--duration-fast) ease-out hover:bg-neutral-200 active:scale-(--es-press) motion-reduce:active:scale-100 dark:hover:bg-neutral-600",
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
className
)}
{...props}
/>
)
}
export {
Toast,
ToastAction,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
}