You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When adding beforeLoad to a route (even an empty one), the router gets mounted and then displays a white page for approx. 400ms before the actual page is rendered.
Also noticed that adding this makes the app a lot more snappy, especially since we perform a lot of middleware/guard checks in beforeLoad() which takes almost no time. The default pendingMinMs is 500 which is pretty long.
Can be disabled for the entire router by setting defaultPendingMinMs: 0,
Reacted by chinda, Alex Ivanov, Luca and Faruk Oruç
Same here, I wasted soo much time digging in this and in the end it was the devtools, after removing them and setting the defaultPendingMinMs: 0 it's much better now, but I feel like it needs a fix
The presence of beforeLoad or loader, even if they are empty (we still await their functions, even if they don’t return a promise) make pendingMinMs “trigger” (if there is a pending component defined).
Even if we have pendingMinMs: 0, the loader flashes quickly.
To goal of this setting is to ensure that loads don’t flash - if we need to see them, we should see them “for a bit”. Because loader or beforeLoad are defined, we determine that we should see the pendingComponent. We don’t really make a difference between a loader that takes 1ms, or one that takes 200ms.
If you only have a beforeLoad or a loader that is sufficiently fast, my suggestion would be to set:
pendingComponent: undefined,
pendingMinMs: 0
for that route. Mostly, I think the defaults are fine because loaders will be used to load data, which can take a couple hundreds of milliseconds, so we’d want to really avoid flashing in the loader (which even happens with pendingMinMs: 0
Setting pendingMinMs and removing the devtool does not work for me. As long as there is a beforeLoad (which does not return a promise), the application shows nonsense loading component.
Since the documentation recommends using beforeLoad as auth guard, it should not have obvious buggy behavior. This library is so superior than current solutions and I don't want to forgo it because of this simple bug.
After a few investigation, I find this issue BOTH a tanstack-router & react suspense issue. I find the extra pending time actually a direct result from React. (I'm using react 19.0.0 and tanstack-router 1.109.2)
Its thrown promise quickly resolves in less than 1ms, but for how long the "loading" appears before the user see "Index"?
According my chrome performance tool, it takes 150ms!
I'm not familiar with React's fiber internals, 150ms, reasonable for remote data fetching, but a painfully long flicker if every "loading" happens on the client-side.
If we stick with the react status-quo, we need to avoid throw new Promise() as long as we can complete the tasks within the current tick.
So, how does tanstack/react-router do? It assumes every beforeLoad returns a promise, and throws it anyways. This causes React to flicker with Suspense with a minimal period of time, even if you set pendingMinMs: 0 or pendingComponent: undefined. (In the later case, it just does not show the pending component and keeps the white screen)
exportconstMatchInner=React.memo(functionMatchInnerImpl({
matchId,}: {matchId: string}): any{// ....// Somehow even if `beforeLoad` is sync, the status is 'pending'if(match.status==='pending'){// We're pending, and if we have a minPendingMs, we need to wait for itconstpendingMinMs=route.options.pendingMinMs??router.options.defaultPendingMinMsif(pendingMinMs&&!router.getMatch(match.id)?.minPendingPromise){// Create a promise that will resolve after the minPendingMs
...
}throwrouter.getMatch(match.id)?.loadPromise// <-- Cause React to suspense}returnout})
Before this is fixed, my suggestion:
For devs: don't use beforeLoad for synchronous functions. Do it in your render function in the old way.
For the authors of this repository: it seems hard to keep beforeLoad the same way because the function does not run synchronously with navigation. My sugguestion is adding a guardSync API that runs synchronously and does not block any rendering at all (or is there already something like that?)
I have an async function, but it's too fast because I'm working with indexed db, but this blank screen is too much for me.
That's why defaultPendingMinMs: 0 is a solution
Activity
[-]`beforeLoad` adds ~400ms delay on page rload[/-][+]`beforeLoad` adds ~400ms delay on page reload[/+]mreinertssgr commentedon Aug 26, 2024
I've been experiencing this too, however my code might be a little different. Rendering the actual app is delayed by almost 1 second.
I also checked the performance tab in devtools and it looks like nothing is happening (the selected range is when nothing is rendered)

0xd8d commentedon Sep 1, 2024
As a temporary fix, add
pendingMinMs: 0.macr commentedon Sep 9, 2024
I just noticed it happening on all the lazy routes.

React profiler shows:
looking at the offending function:
router/packages/react-router/src/Match.tsx
Line 19 in 0e16429
It does wait for
pendingMinMsms before loading the match.Is there a catch to just setting
pendingMinMs: 0?Scooter1337 commentedon Sep 12, 2024
I just saw this issue and added the temporary fix and my app feels way faster now... This needs a fix.
melv-n commentedon Sep 24, 2024
Also noticed that adding this makes the app a lot more snappy, especially since we perform a lot of middleware/guard checks in
beforeLoad()which takes almost no time. The defaultpendingMinMsis500which is pretty long.Can be disabled for the entire router by setting
defaultPendingMinMs: 0,alex-uxify commentedon Nov 6, 2024
Same here, I wasted soo much time digging in this and in the end it was the devtools, after removing them and setting the
defaultPendingMinMs: 0it's much better now, but I feel like it needs a fixTkDodo commentedon Nov 23, 2024
The presence of
beforeLoadorloader, even if they are empty (we stillawaittheir functions, even if they don’t return a promise) makependingMinMs“trigger” (if there is a pending component defined).Even if we have
pendingMinMs: 0, the loader flashes quickly.To goal of this setting is to ensure that loads don’t flash - if we need to see them, we should see them “for a bit”. Because loader or beforeLoad are defined, we determine that we should see the
pendingComponent. We don’t really make a difference between a loader that takes 1ms, or one that takes 200ms.If you only have a
beforeLoador a loader that is sufficiently fast, my suggestion would be to set:for that route. Mostly, I think the defaults are fine because loaders will be used to load data, which can take a couple hundreds of milliseconds, so we’d want to really avoid flashing in the loader (which even happens with
pendingMinMs: 0@schiller-manuel FYI
hanayashiki commentedon Feb 23, 2025
Setting
pendingMinMsand removing the devtool does not work for me. As long as there is abeforeLoad(which does not return a promise), the application shows nonsense loading component.No idea how to workaround this.
{ "@tanstack/react-router": "^1.109.2", "@tanstack/router-devtools": "^1.109.2" }Since the documentation recommends using
beforeLoadas auth guard, it should not have obvious buggy behavior. This library is so superior than current solutions and I don't want to forgo it because of this simple bug.hanayashiki commentedon Feb 23, 2025
After a few investigation, I find this issue BOTH a tanstack-router & react suspense issue. I find the extra pending time actually a direct result from React. (I'm using react 19.0.0 and tanstack-router 1.109.2)
Given a simple component that suspenses
Its thrown promise quickly resolves in less than 1ms, but for how long the "loading" appears before the user see "Index"?
According my chrome performance tool, it takes 150ms!
I'm not familiar with React's fiber internals, 150ms, reasonable for remote data fetching, but a painfully long flicker if every "loading" happens on the client-side.
If we stick with the react status-quo, we need to avoid
throw new Promise()as long as we can complete the tasks within the current tick.So, how does
tanstack/react-routerdo? It assumes everybeforeLoadreturns a promise, and throws it anyways. This causes React to flicker withSuspensewith a minimal period of time, even if you setpendingMinMs: 0orpendingComponent: undefined. (In the later case, it just does not show the pending component and keeps the white screen)Before this is fixed, my suggestion:
beforeLoadfor synchronous functions. Do it in your render function in the old way.beforeLoadthe same way because the function does not run synchronously with navigation. My sugguestion is adding aguardSyncAPI that runs synchronously and does not block any rendering at all (or is there already something like that?)bojandurmic commentedon Mar 27, 2025
This issue still happens for me. Did anyone actually solve this issue?
hanayashiki commentedon Mar 27, 2025
don't use loader merely for sync functions
bojandurmic commentedon Mar 27, 2025
@hanayashiki so do you propose just doing redirects based on auth in the render function? Thanks for the reply by the way!
letstri commentedon Apr 7, 2025
I have an async function, but it's too fast because I'm working with indexed db, but this blank screen is too much for me.
That's why defaultPendingMinMs: 0 is a solution