Toast React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
React toast notifications: mount one Toaster and call toast() from anywhere, with stacking, swipe to dismiss and a pause while you read.
New toasts rise from the edge and stack, the older ones peeking behind the newest. On desktop, hovering the stack fans it out into a list; on a phone, tap it. Swipe a toast toward the edge (down for a bottom stack) or toward its side to dismiss it; a short drag springs back, and a mouse drag works too. Timers pause while you hover, while keyboard focus is inside, while you drag and while the tab is in the background, then resume with the time that was left. Alt+T (⌥T on a Mac) jumps to the newest toast, Tab walks through its buttons, Escape dismisses the focused one. A promise toast turns from loading into success or error in place. Action buttons sit beside the text when the toast has room and move under it when it does not (two buttons, a close button, a narrow phone). On phones toasts span the width with 16px gutters and respect the safe area; under reduced motion they fade instead of moving.
Toast is a free React component in the React feedback components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add toast, together with Button, Spinner, Motion presets, Tones, useShortcut and 2 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add toastUsage
Import Toaster from @/components/ui/toast.
import type { ReactNode } from "react"
import { Button } from "@/components/ui/button"
import { Toaster, toast } from "@/components/ui/toast"
// once, next to the app in the root layout
export function Providers({ children }: { children: ReactNode }) {
return (
<>
{children}
<Toaster richColors />
</>
)
}
// anywhere else
export function PublishButton({ publish }: { publish: () => Promise<{ price: string }> }) {
return (
<Button
onClick={() =>
toast.promise(publish, {
loading: "Publishing the listing…",
success: (data) => `Published for ${data.price}`,
error: "Could not publish the listing",
})
}
>
Publish
</Button>
)
}Props
positiondefault"bottom-right""top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"Where the stack sits on desktop. A bottom stack grows upward and is swiped down; a top one grows downward and is swiped up. Side stacks also swipe toward their side, centered ones either way.
mobilePositiondefault"bottom""top" | "bottom"Below 768px toasts span the width, pinned to the top or the bottom of the screen, above the safe area (home indicator, notch).
expanddefaultfalsebooleanAlways shows the toasts as a list instead of a stack. Timers keep running; hovering or tapping still pauses them.
visibleToastsdefault3numberHow many toasts show at once. Older ones wait in line with their timers stopped and slide in as newer ones leave.
durationdefault4000numberDefault time on screen in ms. A toast can set its own (Infinity keeps it until dismissed); loading toasts wait for their update.
gapdefault12numberSpace between toasts in px while the list is expanded.
offsetdefault24numberDistance from the viewport edges in px on desktop (never less than the safe area).
mobileOffsetdefault16numberSide gutters and edge distance in px on phones. Where the safe area (home indicator, notch) is larger, it wins.
widthdefault356numberToast width in px on desktop. Never wider than the viewport minus the offsets; phones always use the full width.
radiusdefault"md""none" | "sm" | "md" | "lg" | "full"Corner radius of every toast: 0 / 12 / 16 / 24px (full falls back to lg).
richColorsdefaultfalsebooleanTints success, error, warning and info toasts with their tone (an opaque mix over the popover, text and icon in the readable tone color) instead of the neutral surface with a colored icon.
closeButtondefaultfalsebooleanAn X at the top end of every toast. A toast can override it with its own closeButton option.
hotkeydefault"alt+t"stringMoves focus to the newest toast, in mod+k notation. It is appended to the region label ("Notifications Alt+T"). Empty turns it off.
pauseWhenPageIsHiddendefaulttruebooleanStops every timer while the tab is in the background, so nothing expires unseen.
toasterIddefault""stringMakes this toaster show only toasts sent with the same toasterId option, for independent toasters on one page (a canvas, a side panel). Without it, it shows every toast sent without one.
iconsPartial<Record<ToastType, ReactNode>>Replaces the icon per type (success, error, warning, info, loading, default).
labels{ region?: string; close?: string }The region's aria-label (default "Notifications", the hotkey is appended) and the close buttons' label (default "Close").
classNamesPartial<Record<"toaster" | "list" | "toast" | "icon" | "content" | "title" | "description" | "actions" | "action" | "cancel" | "close", string>>Extra classes for the parts. toast is the visible card; toaster and list the fixed region and the stack.
classNamestringExtra classes for the fixed region, merged last. It is rendered in place (not portaled): mount it once in the root layout. className="absolute" pins it inside a relative container.
Parts
toast
functionShows a toast and returns its id. Also toast.success / error / warning / info / loading(message, options), toast.promise(promise, { loading, success, error }, options) which returns the original promise, toast.custom((id) => node, options) and toast.dismiss(id?) (all when no id). Call it again with the same { id } to update a toast in place: with the same type only the options you pass change (progress text), with a new type (loading to success) it starts over from the new options, so a loading toast's Infinity duration or description never sticks to the result. Works outside React and before the Toaster mounts. Signature: (message: ReactNode, options?: ToastOptions) => string.
ToastOptions
typePer toast: icon null hides the type icon, tone or color override the type's color, action { label, onClick, keepOpen } closes the toast unless keepOpen or preventDefault, cancel { label, onClick }, dismissible false blocks swipe, close and Escape, closeButton and richColors override the Toaster's, important makes it assertive (errors already are), onDismiss fires when the user closes it, onAutoClose when it times out. Type: { id, toasterId, description, icon, tone, color, duration, action, cancel, dismissible, closeButton, richColors, important, className, onDismiss, onAutoClose }.
Toast
componentOne toast as a static card (type, title, description, icon, tone, color, action, cancel, closeButton, richColors, radius): what the Toaster renders, usable on its own for mockups and inline confirmations. Props: ToastProps.
useToasts
hookThe toasts on screen, newest first, for a counter or a clear-all button. Signature: (toasterId?: string) => ToastData[].
ToastType
type"default" | "success" | "error" | "warning" | "info" | "loading".
ToastAction
typeThe action button of a toast: label, onClick (closes the toast afterwards unless keepOpen is set or the handler calls preventDefault) and keepOpen.
ToastCancel
typeThe secondary button of a toast: label and an optional onClick; it always closes the toast.
ToastPromiseMessages
typeWhat toast.promise takes besides the promise: loading, success and error, each a node or a function of the result or the error.
Examples
Types, plain and rich
success, info, warning, error and loading on the neutral surface, then with richColors tinting each tone.
Promise
toast.promise shows loading, then success or error in place. Flip the switch to make it fail.
Undo a delete
The row goes at once; Undo in the toast puts it back where it was.
Positions
Six desktop placements; the toaster re-mounts and the toasts follow. Phones always span the width.
Sticky with a close button
duration: Infinity until the connection is back, then the same id turns into a success that times out.
Updating by id
Calling toast.loading again with the same id updates the text and the progress in place.
Brand tone and custom colors
tone="brand" and any CSS color, with richColors or as a colored icon on the neutral surface.
Files
- components/ui/toast.tsx
FAQ
Is the React Toast component free?
Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.
How do I install Toast?
Run npx wingo-ui@latest add toast. It also adds the Wingo UI items it builds on (Button, Spinner, Motion presets, Tones, useShortcut and 2 more) and the npm packages motion and lucide-react. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/toast.json.
How does Toast behave on phones?
On desktop, hovering the stack fans it out into a list; on a phone, tap it. On phones toasts span the width with 16px gutters and respect the safe area; under reduced motion they fade instead of moving.
See also
Used in
More React feedback components
Articles using this component
- Nextjs Contact Form That Sends Email and Blocks Spam
Build it: screens and apps10 min read
- React Notification Center: Inbox, Bell and Toasts
Component guides8 min read
- Chakra UI vs shadcn in 2026: Style Props or Tailwind?
Comparisons and alternatives10 min read
- MUI Alternatives for React and Tailwind in 2026
Comparisons and alternatives10 min read
