Skip to content
LuoForge/Tungsten
Components/Toast·

Toast

since v0.3.0

Transient notification bar — use for non-blocking operation feedback.

Forwards ref to the root live-region <div role={status|alert}> (always mounted).

Install

Add the package and import the component.

pnpm add @hey-mike/tungsten
pnpm add @hey-mike/tungsten
import { Toast } from '@hey-mike/tungsten';
import { Toast } from '@hey-mike/tungsten';

Preview

Same fixtures used by the visual-regression suite.

Usage

apps/docs/app/snapshots/toast/page.tsx

'use client';

import { Toast } from '@hey-mike/tungsten';

const variants = [
  { label: 'default', node: <Toast message="Saved." onDismiss={() => undefined} /> },
  {
    label: 'success',
    node: <Toast variant="success" message="Imported 12 rows." onDismiss={() => undefined} />,
  },
  {
    label: 'error',
    node: <Toast variant="error" message="Upload failed. Try again." onDismiss={() => undefined} />,
  },
  {
    label: 'with-undo',
    node: <Toast message="Item deleted." onUndo={() => undefined} onDismiss={() => undefined} />,
  },
];

export default function ToastSnapshot() {
  return (
    <main data-testid="snapshot-root" className="bg-page text-ink-1 min-h-screen p-8">
      <h1 className="text-ink-2 mb-6 font-mono text-sm uppercase tracking-widest">Toast</h1>
      <dl className="flex flex-col gap-3">
        {variants.map(({ label, node }) => (
          <div key={label} className="flex items-center gap-6">
            <dt className="text-ink-3 font-mono text-xs w-20 shrink-0">{label}</dt>
            {/*
             * transform: translateZ(0) makes this element a containing block for
             * position:fixed descendants — Toast's fixed/inset-x-0/bottom-6 then
             * positions relative to this dd instead of the viewport.
             */}
            <dd className="m-0 flex-1 relative h-20" style={{ transform: 'translateZ(0)' }}>
              {node}
            </dd>
          </div>
        ))}
      </dl>
    </main>
  );
}
'use client';

import { Toast } from '@hey-mike/tungsten';

const variants = [
  { label: 'default', node: <Toast message="Saved." onDismiss={() => undefined} /> },
  {
    label: 'success',
    node: <Toast variant="success" message="Imported 12 rows." onDismiss={() => undefined} />,
  },
  {
    label: 'error',
    node: <Toast variant="error" message="Upload failed. Try again." onDismiss={() => undefined} />,
  },
  {
    label: 'with-undo',
    node: <Toast message="Item deleted." onUndo={() => undefined} onDismiss={() => undefined} />,
  },
];

export default function ToastSnapshot() {
  return (
    <main data-testid="snapshot-root" className="bg-page text-ink-1 min-h-screen p-8">
      <h1 className="text-ink-2 mb-6 font-mono text-sm uppercase tracking-widest">Toast</h1>
      <dl className="flex flex-col gap-3">
        {variants.map(({ label, node }) => (
          <div key={label} className="flex items-center gap-6">
            <dt className="text-ink-3 font-mono text-xs w-20 shrink-0">{label}</dt>
            {/*
             * transform: translateZ(0) makes this element a containing block for
             * position:fixed descendants — Toast's fixed/inset-x-0/bottom-6 then
             * positions relative to this dd instead of the viewport.
             */}
            <dd className="m-0 flex-1 relative h-20" style={{ transform: 'translateZ(0)' }}>
              {node}
            </dd>
          </div>
        ))}
      </dl>
    </main>
  );
}

Props

Surface specific to <Toast />.

PropTypeDefaultDescription
message*string
onUndo(() => void)
onDismiss*() => void
variant"default" | "success" | "error"defaultVisual and semantic variant. Do not change while the toast is mounted — screen readers cache live-region metadata on registration.
visiblebooleantrueControls mount/unmount with an animated enter/exit. Pass false to hide. Defaults to true (always shown).
iconReactElement<unknown, string | JSXElementConstructor<any>> | nullOverride the default status icon. Pass null to suppress the icon entirely. default variant has no default icon.
durationnumberAuto-dismiss delay in ms. The component owns a timer only when this is a finite positive number; omit / Infinity / 0 / negative / NaN all mean no auto-dismiss (the consumer owns dismissal). Pair with {@link ToastProps.pauseOnHover} so an auto-dismissing toast can satisfy WCAG 2.2.1 (Timing Adjustable), which applies once a timeout exceeds 20s.
pauseOnHoverbooleantrueWhen auto-dismissing, pause the timer while the toast is hovered or keyboard-focused and resume on leave/blur — the conventional name covers focus too, so keyboard users aren't excluded. false disables both. Default true. No effect without duration.

Source