Which project does this relate to?
Router
Describe the bug
If a parent route's pendingComponent renders <Outlet />, and the child route below it is still unresolved when the pending component appears, the router subtree renders nothing for the whole navigation. Nothing is thrown, nothing is logged, and no error boundary is hit — the app is blank until the navigation completes.
@tanstack/react-router@1.170.18 (@tanstack/router-core@1.171.15) — last good@tanstack/react-router@1.170.19 (@tanstack/router-core@1.171.16) — first bad@tanstack/react-router@1.170.25 (@tanstack/router-core@1.171.21) — still present
I found that boundary by installing each release in turn and re-running the reproduction. I have not tried to work out which change is responsible.
To be precise about what regressed: on 1.170.18 the same route config also goes blank, but only until the child's own load settles. From 1.170.19 the blank lasts until the entire navigation settles. I measured this by varying the parent and child loader
durations — on 1.170.18 the blank tracks the child and ignores the parent; on 1.170.25 it tracks the whole navigation and ignores the child (Comparison C below).
I could not find anything in the docs stating whether <Outlet /> inside a pendingComponent is supported. The Outlet guide says it "can be rendered anywhere within a route's component tree" and renders null when there is no matching child, and the Outlet API page lists a child's pendingComponent among the things an Outlet renders — but no page addresses an Outletinside a pending component, and I couldn't
find an example that does it. If it is meant to be unsupported, it seems worth saying so and warning in development, because the failure is completely silent.
Complete minimal reproducer
https://github.com/lizatiliguzov/tanstack-minimal-reproduction
Steps to Reproduce the Bug
One assertion — while the parent route is pending, its pendingComponent should be on screen — plus three controls. It fails on 1.170.25 and passes on 1.170.18:
× a pendingComponent WITH <Outlet /> is visible while the parent route is pending
→ expected the pendingComponent to be rendered, but the router subtree rendered nothing
visible text under <RouterProvider> : ""
sibling <div> outside it : "probe rendered outside <RouterProvider>" <- React itself is still rendering
router match statuses : __root__=success, /layout=success, /layout/parent/$id=pending, /layout/parent/$id/child=pending
waited : 5002ms, with the parent route's loader still unresolved
npm run use:working && npm test # 1.170.18 / core 1.171.15 -> 4 passed
npm run use:broken && npm test # 1.170.25 / core 1.171.21 -> 1 failed, 3 passed
The assertion walks the DOM for visible text rather than reading textContent — React retains the old page and hides it with style="display: none !important", so a textContent assertion passes on the broken version and gives a false green.
In a browser: npm run dev, open /, click the one link. The page is blank for ~3 s (the parent route's loader), then loads. The box in the bottom-right is a sibling of <RouterProvider>, so it stays visible while the router subtree renders nothing; the console prints a timeline of every change to that subtree.
Route tree:
__root component: <Outlet />
└── layout component: chrome + <Outlet />
├── / index page with the link
└── /parent/$id loader: 3000ms
pendingMs: 0
pendingComponent: heading + <Outlet />
component: heading + <Outlet />
└── child loader: 50ms
No defaultPendingComponent on the router. pendingMs: 0 is load-bearing: the precondition is that the child is still unresolved when the pending component appears. With the default pendingMs of 1000 a 50 ms child resolves first and there is no blank; a child still loading at that point blanks the same way (measured with a 2000 ms child loader and no pendingMs override: blank 1012 → 3012 ms).
Comparison A — version. Blank duration, measured from the click:
| version | blank |
|---|
| 1.170.18 / core 1.171.15 | ~510 ms, then the pending component is visible for the rest of the load |
| 1.170.19 / core 1.171.16 | ~3010 ms — the entire navigation |
| 1.170.25 / core 1.171.21 | ~3010 ms — the entire navigation |
Comparison B — the <Outlet />. Same version (1.170.25):
| pending component | result |
|---|
with <Outlet /> (npm run dev) | blank for the entire navigation |
without <Outlet /> (npm run dev:no-outlet) | pending component visible immediately and for the whole load |
Comparison C — what bounds the blank. Unlike A and B this one is not scripted: I
measured it by editing the parentLoader / childLoader defaults in src/app.tsx (they
are options on the route factory, not env knobs). The cell is how long the app stayed blank:
| 1.170.18 | 1.170.25 |
|---|
| parent 3000, child 5 | 511 ms | 3011 ms |
| parent 3000, child 800 | 812 ms | 3015 ms |
| parent 3000, child 2500 | 2511 ms | 3011 ms |
| parent 6000, child 50 | 511 ms | 6017 ms |
| parent 1000, child 50 | 511 ms | 1016 ms |
| parent 500, child 2500 | 2511 ms | 2510 ms |
Expected behavior
While the parent route is pending, I expected its pendingComponent to be visible — as it is on 1.170.18, and as it is on 1.170.25 the moment the <Outlet /> is removed. Instead nothing renders at all for the whole navigation.
If rendering <Outlet /> inside a pendingComponent is not supported, I would expect the Outlet to render null there — which is what it already does when there is no matching child — or at minimum a development warning, rather than silently blanking the app.
Screenshots or Videos
No response
Platform
- Router / Start Version: @tanstack/react-router 1.170.25 (@tanstack/router-core 1.171.21); last good 1.170.18 (core 1.171.15), first bad 1.170.19 (core 1.171.16)
- OS: macOS (Darwin 25.5.0)
- Browser: Chromium 151 (Playwright); the headless test runs in jsdom 30
- Browser Version: 151
- Bundler: vite
- Bundler Version: 7.3.6
React 19.2.8, Node 24.15.0, vitest 4.1.10. No router plugin, no file-based routing, no TanStack Start — plain code-based routes.
Additional context
Full matrix. Client-side navigation from / to /parent/1/child, parent loader 3000 ms:
| router | <Outlet /> in pending | child route | StrictMode | result |
|---|
| 1.170.18 | no | own loader | on | pending UI at ~5 ms, no blank |
| 1.170.18 | yes | none (resolves synchronously) | on | pending UI + child at ~6 ms, no blank |
| 1.170.18 | yes | own loader | on | blank ~510 ms, then pending UI |
| 1.170.19 | yes | own loader | on | blank ~3010 ms |
| 1.170.25 | no | own loader | on | pending UI at ~6 ms, no blank |
| 1.170.25 | yes | none (resolves synchronously) | on | pending UI + child at ~6 ms, no blank |
| 1.170.25 | yes | own loader | on | blank ~3010 ms |
| 1.170.25 | yes | own loader | off | blank ~3010 ms |
| 1.170.25 | yes | code-split component | on | blank ~3010 ms |
| 1.170.25 | yes, wrapped in a local <Suspense> | own loader | on | pending UI + local fallback, no blank |
| 1.170.25 | yes, plus a router defaultPendingComponent | own loader | on | pending UI + default pending in the child slot, no blank |
StrictMode is not involved. A fresh full page load directly on /parent/1/child behaves the same. No console errors, no React warnings, no unhandled rejections during the blank.
The ~510 ms floor on 1.170.18 tracks defaultPendingMinMs (500 by default): set defaultPendingMinMs: 1200 and the floor moves to 1216 ms.
The last two matrix rows are both usable workarounds: wrapping the <Outlet /> in the pending component in a local <Suspense>, or setting a router-level defaultPendingComponent, removes the blank entirely. With the second, the child slot shows the default pending component for the rest of the load.
Existing issues I looked at.#7986 and #7958 are "pending UI shows too often" rather than a blank, and 1.170.22 lists a fix for them — this still reproduces on 1.170.25. #4759 and #7961 are about a blank frame before the pending component appears on initial load, not a blank that lasts the whole navigation. I may have missed something, so apologies if this is a duplicate.
Which project does this relate to?
Router
Describe the bug
If a parent route's
pendingComponentrenders<Outlet />, and the child route below it is still unresolved when the pending component appears, the router subtree renders nothing for the whole navigation. Nothing is thrown, nothing is logged, and no error boundary is hit — the app is blank until the navigation completes.@tanstack/react-router@1.170.18(@tanstack/router-core@1.171.15) — last good@tanstack/react-router@1.170.19(@tanstack/router-core@1.171.16) — first bad@tanstack/react-router@1.170.25(@tanstack/router-core@1.171.21) — still presentI found that boundary by installing each release in turn and re-running the reproduction. I have not tried to work out which change is responsible.
To be precise about what regressed: on 1.170.18 the same route config also goes blank, but only until the child's own load settles. From 1.170.19 the blank lasts until the entire navigation settles. I measured this by varying the parent and child loader
durations — on 1.170.18 the blank tracks the child and ignores the parent; on 1.170.25 it tracks the whole navigation and ignores the child (Comparison C below).
I could not find anything in the docs stating whether
<Outlet />inside apendingComponentis supported. TheOutletguide says it "can be rendered anywhere within a route's component tree" and rendersnullwhen there is no matching child, and theOutletAPI page lists a child'spendingComponentamong the things anOutletrenders — but no page addresses anOutletinside a pending component, and I couldn'tfind an example that does it. If it is meant to be unsupported, it seems worth saying so and warning in development, because the failure is completely silent.
Complete minimal reproducer
https://github.com/lizatiliguzov/tanstack-minimal-reproduction
Steps to Reproduce the Bug
One assertion — while the parent route is pending, its
pendingComponentshould be on screen — plus three controls. It fails on 1.170.25 and passes on 1.170.18:The assertion walks the DOM for visible text rather than reading
textContent— React retains the old page and hides it withstyle="display: none !important", so atextContentassertion passes on the broken version and gives a false green.In a browser:
npm run dev, open/, click the one link. The page is blank for ~3 s (the parent route's loader), then loads. The box in the bottom-right is a sibling of<RouterProvider>, so it stays visible while the router subtree renders nothing; the console prints a timeline of every change to that subtree.Route tree:
No
defaultPendingComponenton the router.pendingMs: 0is load-bearing: the precondition is that the child is still unresolved when the pending component appears. With the defaultpendingMsof 1000 a 50 ms child resolves first and there is no blank; a child still loading at that point blanks the same way (measured with a 2000 ms child loader and nopendingMsoverride: blank 1012 → 3012 ms).Comparison A — version. Blank duration, measured from the click:
Comparison B — the
<Outlet />. Same version (1.170.25):<Outlet />(npm run dev)<Outlet />(npm run dev:no-outlet)Comparison C — what bounds the blank. Unlike A and B this one is not scripted: I
measured it by editing the
parentLoader/childLoaderdefaults insrc/app.tsx(theyare options on the route factory, not env knobs). The cell is how long the app stayed blank:
Expected behavior
While the parent route is pending, I expected its
pendingComponentto be visible — as it is on 1.170.18, and as it is on 1.170.25 the moment the<Outlet />is removed. Instead nothing renders at all for the whole navigation.If rendering
<Outlet />inside apendingComponentis not supported, I would expect theOutletto rendernullthere — which is what it already does when there is no matching child — or at minimum a development warning, rather than silently blanking the app.Screenshots or Videos
No response
Platform
React 19.2.8, Node 24.15.0, vitest 4.1.10. No router plugin, no file-based routing, no TanStack Start — plain code-based routes.
Additional context
Full matrix. Client-side navigation from
/to/parent/1/child, parent loader 3000 ms:<Outlet />in pending<Suspense>defaultPendingComponentStrictMode is not involved. A fresh full page load directly on
/parent/1/childbehaves the same. No console errors, no React warnings, no unhandled rejections during the blank.The ~510 ms floor on 1.170.18 tracks
defaultPendingMinMs(500 by default): setdefaultPendingMinMs: 1200and the floor moves to 1216 ms.The last two matrix rows are both usable workarounds: wrapping the
<Outlet />in the pending component in a local<Suspense>, or setting a router-leveldefaultPendingComponent, removes the blank entirely. With the second, the child slot shows the default pending component for the rest of the load.Existing issues I looked at.#7986 and #7958 are "pending UI shows too often" rather than a blank, and 1.170.22 lists a fix for them — this still reproduces on 1.170.25. #4759 and #7961 are about a blank frame before the pending component appears on initial load, not a blank that lasts the whole navigation. I may have missed something, so apologies if this is a duplicate.