Which project does this relate to?
TanStack Router (@tanstack/react-router@1.170.18, with @tanstack/router-core@1.171.15 transitively installed)
Describe the bug
When a navigation enables viewTransition, RouterCore.startViewTransition calls document.startViewTransition(...) but discards the returned ViewTransition object.
Browsers may reject ViewTransition.ready or ViewTransition.finished for normal lifecycle conditions, including a document becoming hidden or a transition being superseded. Because the promises have no rejection handlers, these can become unhandled promise rejections even though the router commits the new location successfully.
One Chromium case is:
InvalidStateError: Transition was aborted because of invalid state
Complete minimal reproduction
Repository: https://github.com/David-0x221Eight/tanstack-router-view-transition-repro
Versions:
@tanstack/react-router@1.170.18
@tanstack/router-core@1.171.15 (transitive dependency)
react@19.2.8
vite@8.1.5
Steps in Chrome:
- Run
npm install and npm run dev.
- Open the local page and the DevTools console.
- Click Arm reproduction.
- Switch to another browser tab, wait briefly, and return.
The app uses the real browser API. It never replaces, wraps, or mocks document.startViewTransition. It listens for the real visibilitychange event, then calls:
router.navigate({
to: "/next",
viewTransition: true,
});
while the document is genuinely hidden. Its unhandledrejection observer only displays the rejection and does not call preventDefault().
Observed result in Chrome:
/next commits and the destination content renders;
- the page receives an unhandled rejection;
- Chrome reports
InvalidStateError: Transition was aborted because of invalid state in the DevTools console.
Expected behavior
Router should consume rejections from the lifecycle promises it creates. Expected transition aborts should not become global errors; unexpected failures should remain observable.
A possible shape is:
const transition = document.startViewTransition(startViewTransitionParams)
void transition.ready.catch(handleViewTransitionRejection)
void transition.finished.catch(handleViewTransitionRejection)
For example, the handler can ignore AbortError and the Chromium InvalidStateError above, while forwarding other errors to globalThis.reportError (or console.error when unavailable).
Which project does this relate to?
TanStack Router (
@tanstack/react-router@1.170.18, with@tanstack/router-core@1.171.15transitively installed)Describe the bug
When a navigation enables
viewTransition,RouterCore.startViewTransitioncallsdocument.startViewTransition(...)but discards the returnedViewTransitionobject.Browsers may reject
ViewTransition.readyorViewTransition.finishedfor normal lifecycle conditions, including a document becoming hidden or a transition being superseded. Because the promises have no rejection handlers, these can become unhandled promise rejections even though the router commits the new location successfully.One Chromium case is:
Complete minimal reproduction
Repository: https://github.com/David-0x221Eight/tanstack-router-view-transition-repro
Versions:
@tanstack/react-router@1.170.18@tanstack/router-core@1.171.15(transitive dependency)react@19.2.8vite@8.1.5Steps in Chrome:
npm installandnpm run dev.The app uses the real browser API. It never replaces, wraps, or mocks
document.startViewTransition. It listens for the realvisibilitychangeevent, then calls:while the document is genuinely hidden. Its
unhandledrejectionobserver only displays the rejection and does not callpreventDefault().Observed result in Chrome:
/nextcommits and the destination content renders;InvalidStateError: Transition was aborted because of invalid statein the DevTools console.Expected behavior
Router should consume rejections from the lifecycle promises it creates. Expected transition aborts should not become global errors; unexpected failures should remain observable.
A possible shape is:
For example, the handler can ignore
AbortErrorand the ChromiumInvalidStateErrorabove, while forwarding other errors toglobalThis.reportError(orconsole.errorwhen unavailable).