Skip to content

Toast component

Toast is a provider-based notification system: mount ToastProvider once near the root of your app, then call the imperative toast(...) function from anywhere — a click handler, a data-fetching hook, deep inside a component tree — without threading state or props down to get there. It supports a promise-aware mode that walks a toast through loading → success/error automatically.

Feedback35 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Toast actually gets used — each one a real, self-contained example.

Typed shortcuts

toast.success / error / warning / info set the type and matching icon. Mount ToastProvider once near your app root; call toast() from anywhere.

// app root
<ToastProvider>
  <App />
</ToastProvider>

// anywhere
import { toast } from "oks-ui/toast";

toast.success("Saved");
toast.error("Upload failed");
toast.warning("Storage almost full");
toast.info("A new version is available");

Title and description

Pass a description for a second line of context beneath the title.

toast.success("Profile updated", {
  description: "Your changes are visible to your team.",
});

Toast with an action

An action button turns a notification into something the user can act on — an undo, a reload. Pair it with duration: null so it waits.

toast("Message archived", {
  action: { label: "Undo", onClick: () => restore() },
  duration: null,
});

Promise-aware

Hand toast.promise a promise and it walks loading → success/error on its own, so the loading state can't drift from the real request.

toast.promise(saveProfile(data), {
  loading: { message: "Saving…" },
  success: () => ({ message: "Profile saved" }),
  error: () => ({ message: "Couldn't save profile" }),
});

Persistent until dismissed

persistent removes the auto-dismiss timer — pair with dismissible so the user still has a way to close it. Reserve it for things they must act on.

toast.error("Payment failed", {
  description: "Update your card to keep your subscription.",
  persistent: true,
  dismissible: true,
});

One provider, called from anywhere

Mount ToastProvider once, high in the tree — it renders a portaled region (position defaults to bottom-right) that every toast() call anywhere in the app renders into. There's no toast context to consume and no hook to call at the use-site; toast(...) is a plain imported function.

// app root
<ToastProvider position="top-right" maxVisibleToasts={3}>
  <App />
</ToastProvider>

// anywhere else in the tree
import { toast } from "oks-ui";

const SaveButton = () => {
  return (
    <Button onPress={() => toast.success("Saved", { description: "Your changes were saved." })}>
      Save
    </Button>
  );
};

Typed shortcuts and full control

toast.success/toast.error/toast.warning/toast.info are shortcuts that set both type and the matching default icon. For anything not covered by a shortcut, call toast(options) directly with the full ToastOptions shape — variant, size, duration, a dismiss/action button, whatever you need.

toast.success("Copied to clipboard");
toast.error("Upload failed", { description: "The file exceeded 10 MB.", persistent: true });

toast({
  title: "Update available",
  description: "A new version is ready to install.",
  action: { label: "Reload", onClick: () => window.location.reload() },
  duration: null,
});

Promise-aware toasts

Pass a promise and toast walks through loading → success/error on its own — no manual success()/error() calls to keep in sync with the actual request. It shows a loading state immediately, then swaps to success or error once the promise settles.

toast.promise(saveProfile(formData), {
  loading: "Saving…",
  success: "Profile saved",
  error: "Couldn't save profile",
});

Duration, persistence, and dismissal

Every toast auto-dismisses after defaultDuration (6s) unless you override duration per-toast, or set persistent to remove the auto-dismiss timer entirely (typically paired with dismissible so the user still has a way to close it manually). showDurationBar renders a shrinking progress bar so the countdown is visible, not just implied.

toast.warning("Session expiring soon", { duration: 10000, showDurationBar: true });
toast.error("Payment failed", { persistent: true, dismissible: true });

Props

The full public API for Toast. Every prop is stable and intentionally typed.

Provider — Content

children

ReactNode

default: —

Your app, rendered inside the provider.

Provider — Layout & positioning

position

"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"

default: "bottom-right"

Where the toast region renders.

maxToasts

number

default: 12

Total toasts kept in the queue (visible + pending).

maxVisibleToasts

number

default: 3

Toasts shown at once before stacking/queuing.

toastOffset

number

default: 0

Extra offset from the viewport edge.

container

Element | DocumentFragment | null

default: —

Custom portal mount target.

Provider — Behavior & states

defaultDuration

number

default: 6000

Auto-dismiss duration in ms, unless overridden per-toast or persistent.

toastProps

ToastOptions

default: —

Default options applied to every toast from this provider.

Provider — Motion

motion

"auto" | "reduced" | "default"

default: "auto"

Animation mode; "auto" respects prefers-reduced-motion.

disableAnimation

boolean

default: false

Disables all toast animation.

Provider — Styling

regionProps

ToastRegionProps

default: —

Extra props for the toast region container.

className

string

default: —

Class applied to the toast region.

style

CSSProperties

default: —

Inline styles for the toast region.

Toast — Content

title

ReactNode

default: —

Toast title.

description / message

ReactNode

default: —

Toast body content.

icon

ReactNode

default: —

Overrides the default type-derived icon.

showIcon / hideIcon

boolean

default: shown

Show or hide the icon slot; hideIcon wins when both are set. The icon is shown by default and re-evaluated when type or color changes.

loadingComponent

ReactNode

default: —

Shown in the icon slot while type is "loading" (for example during a promise toast), instead of the status icon.

endContent

ReactNode

default: —

Content rendered at the end of the toast.

action

{ label: string; onClick?: () => void }

default: —

An inline action button.

Toast — Appearance

type

"default" | "info" | "success" | "warning" | "error" | "loading"

default: derived from color, else "default"

Semantic type, also picks the default icon.

color

ToastColor

default: —

Semantic color.

variant

"solid" | "soft" | "flat" | "bordered"

default: "flat"

Visual style variant.

radius

OksRadius

default: "md"

Border radius.

size

OksSize

default: "md"

Toast size.

Toast — Layout & positioning

position

ToastPosition

default: provider's position

Per-toast position override.

Toast — Behavior & states

duration

number | null

default: 6000

Auto-dismiss duration in ms; null disables auto-dismiss.

persistent

boolean

default: false

Never auto-dismisses.

showDurationBar

boolean

default: false

Shows a countdown progress bar.

dismissible

boolean

default: true

Shows a close button.

promise

Promise<unknown>

default: —

Drives the promise-aware loading/success/error flow.

Toast — Events

onDismiss

(id: ToastId) => void

default: —

Called when the toast is dismissed.

Toast — Styling

closeIcon

ReactNode

default: —

Overrides the default close icon.

classNames

ToastClassNames

default: —

Per-slot class overrides.

className

string

default: —

Class applied to this toast's root element.

Accessibility

  • The toast region is a live region, so screen readers announce new toasts as they appear without the user needing to navigate to them.
  • When dismissible (the default), the close button is a real, keyboard-operable button with its own accessible name.
  • Prefer persistent + a visible action for anything the user actually needs to act on — an auto-dismissing toast that disappears before it's read defeats its own purpose for anyone reading slowly or using a screen reader.

Best practices

  • Keep toast text to one short sentence; if a message needs a paragraph of explanation, it probably belongs in an inline Alert instead.
  • Reserve persistent for toasts the user must act on (a failed payment, a required re-auth) — persistent success confirmations just pile up.
  • Use toast.promise for anything already returning a promise (a save, an upload, a fetch) instead of manually calling toast.success/toast.error in a .then()/.catch() — it keeps the loading state honest.
  • Only one ToastProvider is meant to be mounted per toast "queue" — nesting multiple providers doesn't give you independent queues, since toast() targets whichever provider is currently mounted.
  • For a message tied to specific content on the page (a form field error, a section-level warning), use Alert instead — Toast is for transient, page-level notifications, not content that should stay anchored in the layout.