Skip to content

Toast docs wrapUpdate example produces unhandled AbortError rejections when toasts overlap #10337

Description

@awkale

Provide a general summary of the issue here

The Toast documentation and starters/docs/src/Toast.tsx recommend a wrapUpdate that wraps queue updates in document.startViewTransition():

wrapUpdate(fn) {
  if ('startViewTransition' in document) {
    document.startViewTransition(() => {
      flushSync(fn);
    });
  } else {
    fn();
  }
}

Every queue.add() and every auto-dismiss triggers wrapUpdate, so whenever two toasts overlap, the second startViewTransition call preempts the first. Per the View Transitions spec, the skipped transition rejects its ready promise with AbortError: Transition was skipped (finished resolves for skipped transitions). The example attaches no rejection handlers, so apps following the docs get one unhandled promise rejection per overlapping toast.

🤔 Expected Behavior?

The documented pattern should not produce unhandled promise rejections under normal usage (two or more concurrent toasts, or a toast added while another auto-dismisses).

😯 Current Behavior

One uncaught AbortError: Transition was skipped per overlapping toast. Each transition also snapshots the entire document, so toast bursts queue continuous full-page snapshots.

💁 Possible Solution

Minimal docs fix — handle all three transition promises:

const transition = document.startViewTransition(() => { flushSync(fn); });
transition.finished.catch(() => {});
transition.ready.catch(() => {});
transition.updateCallbackDone.catch(() => {});

Optionally, the example could also skip starting a new transition while one is in flight, applying fn() directly. This avoids back-to-back full-document snapshots during bursts, and is safe with respect to queue semantics: the queue mutates its state before notifying subscribers, and subscribers read live state via useSyncExternalStore, so applying a notification directly always converges on current state.

🔦 Context

Surfaced in production error tracking (console flooding) and as DOM-stability timeouts in downstream Cypress suites when pages fire paired "loading → success" toasts. Any consumer flow with ≥2 concurrent toasts hits it.

🖥️ Steps to Reproduce

  1. Use the docs Toast starter unmodified (Vanilla CSS or Tailwind).
  2. Click the queue.add(...) button twice quickly (or add two toasts with overlapping timeouts).
  3. Observe uncaught AbortError: Transition was skipped in the console.

Version

react-aria-components@1.17.0 (react-aria@3.48.0 / react-stately@3.46.0) — the example is still present on main starters.

What browsers are you seeing the problem on?

Chrome (any View Transitions-capable engine)

What operating system are you using?

macOS

🧢 Your Company/Team

dv01

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions