facebook / react · Issue No. 37669
React version: 19.3.0 (also 19.3.0-canary-59aff3e1-20260918, 0.0.0-experimental-59aff3e1-20260918, and the canary vendored in Next.js 16.3.5)
Summary
customizeViewTransitionError in packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js suppresses the InvalidStateError a browser raises when a view transition is skipped on a hidden document, but only by exact message equality. Since Chrome/Edge 153 (stable 2026-09-08) Blink appends the skip reason to the generic message, so the comparison fails and the error is passed to onRecoverableError on every skipped transition in a background tab. Firefox's wording was never in the list.
Messages observed in production:
Transition was aborted because of invalid state. Document hidden (Chrome/Edge 153)Transition was aborted because of invalid state. Viewport size changed (Chrome/Edge 153)Transition was aborted because of invalid state (Chrome/Edge ≤ 152, suppressed today)Skipped ViewTransition due to document being hidden (Firefox 153–156, not suppressed)Blink builds the string as prefix + skip reason (FormatExceptionMessage in dom_view_transition.cc), and on a hidden document only ready rejects, so the rejection goes through React's handler and the equality check is the only thing deciding suppress vs report. In one production app this went from zero to about 240 sessions/week starting 2026-09-09.
Steps to reproduce
Real browser: render a <ViewTransition> whose update is triggered by a timer, switch to another tab before it fires, come back. On Chrome 153 onRecoverableError receives the DOMException; on ≤ 152 nothing is reported.
Deterministic, browser-independent (this is how I verified 19.3.0):
// before React loads: mimic Chromium's DidSkipTransition for a hidden document
const msg = "Transition was aborted because of invalid state. Document hidden";
document.startViewTransition = (opts) => {
const update = typeof opts === "function" ? opts : opts.update;
const updateDone = Promise.resolve().then(() => update());
return { ready: Promise.reject(new DOMException(msg, "InvalidStateError")),
finished: updateDone.then(() => undefined), updateCallbackDone: updateDone,
skipTransition() {}, types: new Set() };
};
// then: createRoot(el, { onRecoverableError: console.error }) rendering a <ViewTransition>
// updated inside startTransition every second
Result with react-dom 19.3.0: the bare message logs nothing; the ". Document hidden" message and the Firefox message reach onRecoverableError on every transition.
Current behavior
Every skipped transition in a background tab surfaces as a recoverable error on Chrome/Edge 153+ and Firefox, which frameworks forward to reportError and error trackers record as a crash.
Expected behavior
The hidden-document skip stays suppressed as it was on Chrome ≤ 152. Suggested fix: match the Chrome prefix (error.message.startsWith("Transition was aborted because of invalid state")) and add Firefox's "Skipped ViewTransition due to document being hidden". Happy to open a PR.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.