Desktop Notifications
Toast notifications for desktop and web — brief, non-blocking messages that appear in a screen corner to confirm actions, surface warnings, or report errors. Portaled to document.body; never interrupt flow.
Preview
Toast appears bottom-right and auto-dismisses after 4 s
Variants
| Variant | Icon color | Use when |
|---|---|---|
| info | Blue | Neutral updates, tips, new features. |
| success | Green | Confirmed saves, completed tasks, successful sends. |
| warning | Amber | Non-blocking issues the user should know about but can continue. |
| error | Red | Failed operations. Set duration=0 so it persists until the user dismisses. |
Position
| Value | Best for |
|---|---|
| bottom-right | Default. Non-intrusive; far from primary content focus. |
| top-right | Apps with bottom-anchored toolbars or navigation. |
| bottom-center | Mobile web or narrow viewports. |
| top-center | Critical alerts that need to be seen immediately. |
Implementation
The Toast renders via createPortal to document.body — it is never clipped by overflow: hidden ancestors. Auto-dismiss runs a setTimeout that is cleared if the user dismisses early or the component unmounts.
Toast.tsx
tsx
import { useState } from "react";
import { Toast } from "@/components/ui/Toast";
import { Button } from "@/components/ui/Button";
export function Example() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Save changes</Button>
<Toast
open={open}
onClose={() => setOpen(false)}
variant="success"
title="Changes saved"
description="Your profile has been updated."
position="bottom-right"
action={{ label: "Undo", onClick: () => console.log("undo") }}
/>
</>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
open* | boolean | — | Controls visibility. Toast unmounts when false. |
onClose* | () => void | — | Called when the timer expires or the user dismisses. |
title* | string | — | Primary message. Keep under 60 chars. |
description | string | — | Supporting detail. Optional; 1–2 lines. |
variant | "info" | "success" | "warning" | "error" | "info" | Controls icon and icon color. |
duration | number | 4000 | Auto-dismiss delay in ms. Pass 0 to disable auto-dismiss. |
position | "top-right" | "bottom-right" | "bottom-center" | "top-center" | "bottom-right" | Screen corner where the toast appears. |
action | { label: string; onClick: () => void } | — | Optional inline action button. |
Accessibility
- →Use role="status" and aria-live="polite" for informational toasts. Screen readers announce the content at the next natural pause.
- →For urgent error toasts, switch to role="alert" and aria-live="assertive" — the announcement interrupts the current reading flow.
- →aria-atomic="true" ensures the full toast content is read at once rather than character by character as text updates.
- →Never auto-dismiss error toasts (duration=0). Users may need to read error messages carefully or copy details. Auto-dismiss only low-urgency variants.
- →The close button must be reachable by keyboard. Focus should not automatically move to the toast — it is supplementary information, not a blocking dialog.
- →Respect prefers-reduced-motion: remove slide-in animation while keeping the visual appearance. The toast should still appear; just without motion.