opsinjs
ComponentsFeedback

Toast

A brief, self-dismissing confirmation of the reader's own action, built on Base UI Toast.

Preview

Theme
Density
Text
Status
toast · base base · style base-lyraOpen under the product theme

Installation

pnpm dlx shadcn@latest add @opsinjs/toast

The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.

Usage

import { Toast } from "@/components/ui/toast"
import { Toast as ToastManager } from "@base-ui/react/toast"
function SaveButton() {
  const manager = ToastManager.useToastManager()
  return (
    <button onClick={() => manager.add({ title: "Saved" })}>Save note</button>
  )
}

function Screen() {
  return (
    <Toast position="bottom-right">
      <SaveButton />
    </Toast>
  )
}

When to use it

Use it when

  • Confirming an action the reader took, such as a note saved or a reminder set, where missing it costs nothing.
  • Acknowledging a background action that completed, such as a sync finishing, which the reader loses nothing by missing.
  • Offering a brief optional way to reverse the action, such as an Undo the reader may take or ignore.

Do not use it when

  • The message is a reading, a result, or anything about the reader's health they must not miss.Use alert-banner instead.
  • The message needs attention now and stays until the reader has dealt with it.Use alert-banner instead.
  • The information is a standing explanation, note or caveat that belongs on the surface while they read.Use callout instead.

Anatomy

  1. ToastThe region. Renders Base UI's provider and a fixed viewport, and mounts children inside so they can push a toast.Controlled by position
  2. Toast.ViewportThe live region the stack sits in: role="region", aria-live="polite", named by label, reachable with F6.Controlled by label
  3. Toast.ItemOne toast, role="dialog" labelled by its title. Card material, hairline and overlay shadow, so it reads as lifted.
  4. Toast.TitleThe confirmation itself, such as "Saved". The full foreground, drawn only when the pushed toast carries one.
  5. Toast.DescriptionAn optional second line of detail. The muted role at the footnote step.
  6. Toast.ActionAn optional quiet text control such as Undo, drawn when the pushed toast carries actionProps.
  7. Toast.CloseThe dismiss control, a lucide X named by closeLabel, floored at the 44px target on both axes.Controlled by closeLabel

Examples

A save confirmation

A button pushes a "Saved" toast that stays a few seconds and then leaves. The words are fictional and name nothing measured.

Theme
Density
Text
Status
toast-a-save-confirmation · base base · style base-lyraOpen under the product theme

With an action

actionProps adds one quiet control beside the message, so a reader who removed a note by mistake can reverse it. Here Undo only closes the toast.

Theme
Density
Text
Status
toast-with-an-action · base base · style base-lyraOpen under the product theme

Content guidelines

Keep a toast to the fact of the action, past tense, a word or two: "Saved", "Reminder set", "Note removed". Never write a clinical word, a reading or a level of urgency into one. Give an action a verb, and make it safe to ignore.

Do

"Saved" with an optional "Undo". The action still happened if the reader looks away.

Don’t

A toast carrying a number, a result or an urgency. Whoever needs it most misses it.

Accessibility

Audited against WCAG 2.2 AA in a source pass and a rendered pass that together raised no axe violation, no target-size hit and no 320px reflow failure. This is an author-run audit, not an independent review; clinical review is pending.

The audit found nothing in the source to change. Roles, names, the polite live region, pause on hover and focus, Escape and focus return all come correctly from @base-ui/react. The X icon is aria-hidden and the close carries a translatable aria-label. Colours are role tokens, type sizes are text-opsin-* tokens, and the controls floor their hit area at --opsin-target-minimum.

Residual items to know. A toast pushed with a description and no title yields a dialog with no accessible name, because ToastList rightly emits no empty title; the default and both examples always pass a title, and a description-only toast is still announced by the live region through aria-describedby. Whether an appearing toast overlaps a control focused elsewhere is a product-layout question outside component scope. Contrast pairs are unmeasured, so the report below carries what was measured.

KeyActionNotes
F6Moves focus to the toast viewportA Base UI global shortcut, so the stack is reachable without tabbing the page.
TabMoves between the controls in the focused toastTwo stops with an action, one without. Focus stays in the toast.
Shift+TabMoves back through the controls in the focused toastThe same stops in reverse.
Enter, SpaceActivates the focused controlThe close control or the action, both native buttons.
EscapeDismisses the focused toastSo a reader never waits on the auto-dismiss timer.
PairThemeAPCA LcWCAG 2.2Floor
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below floor
body text on the canvas materiallight104.717.96:1Pass
body text on the canvas materialdark-100.518.00:1Pass
body text on the inset materiallight98.116.32:1Pass
body text on the inset materialdark-100.117.18:1Pass
body text on the card materiallight104.717.96:1Pass
body text on the card materialdark-99.616.32:1Pass
body text on the raised materiallight104.717.96:1Pass
body text on the raised materialdark-97.513.77:1Pass
body text on the sheet material over the darkest backdroplight99.916.76:1Pass
body text on the sheet material over the darkest backdropdark-99.716.47:1Pass
body text on the sheet material over the lightest backdroplight104.717.96:1Pass
body text on the sheet material over the lightest backdropdark-80.86.07:1Pass
body text on the overlay material over the darkest backdroplight85.913.48:1Pass
body text on the overlay material over the darkest backdropdark-99.916.77:1Pass
body text on the overlay material over the lightest backdroplight104.717.96:1Pass
body text on the overlay material over the lightest backdropdark-54.22.69:1Below floor

These are the measured token pairs this component draws colour from, not a measurement of the component itself.

API reference

Prop

Type

Generated from ToastProps in registry/bases/base/toast.tsx.

position defaults to bottom-right, limit to three and timeout to 5000ms. Passing timeout={0} stops every toast dismissing itself and raises a development warning, because a message that must persist is an AlertBanner or a Callout. label and closeLabel are props so they can be translated, and both default to English.

  • AlertBanner is for a message that needs attention now and stays until the reader deals with it.
  • Callout sets a standing note or caveat apart for as long as the reader is on the surface.

On this page