router icon indicating copy to clipboard operation
router copied to clipboard

Root route is rendered before loaders are finished

Open cristi-lng opened this issue 1 year ago • 10 comments

Describe the bug

It seems that the root route component is rendered before all the loaders finished their work.

If I understand correctly no component should be rendered until all loaders are finished. Is this still true?

When I test with a default pending component the behaviour seems to be correct. So, the problem is only when there is no pending component.

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-vkppeu?file=src%2Fmain.tsx

Steps to Reproduce the Bug or Issue

  1. Check the demo from the link
  2. See how the page is loaded and only the root component is displayed before the loaders finish
  3. You can toggle the defaultPendingComponent to test in pending mode

Expected behavior

If no pending is used, I am expecting to see everything rendered after the loaders are finished.

Screenshots or Videos

No response

Platform

  • OS: macOS
  • Browser: Chrome
  • @tanstack/react-router: 1.34.9

Additional context

No response

cristi-lng avatar Jun 04 '24 16:06 cristi-lng

If I understand correctly no component should be rendered until all loaders are finished. Is this still true?

a route will render as soon as its loaders are finished. So the root route will render as soon as the loaders of the root route are done. It will not wait for loaders of child routes.

there is no aggregation of loaders

TkDodo avatar Jun 04 '24 16:06 TkDodo

Ok, but if this is the case why isn't the layout route from my example rendered as well? Because it doesn't have any loaders.

Also, if for example, the loader from a child route finishes before the one from the parent I still suspect that the child won't be rendered until the parent loader is finished.

I believe that the behaviour changed somehow because if we run with the old version of the router (1.22.9) it waits for all the loaders before rendering.

cristi-lng avatar Jun 04 '24 16:06 cristi-lng

This is mainly because of our homemade transition framework that "holds" state updates to the rendered state until everything is ready. Sounds like the root route is escaping that framework. I'll work on this now.

tannerlinsley avatar Jul 08 '24 23:07 tannerlinsley

More information here that needs to be considered, specifically for client-side SPAs:

Currently:

  • The root route is only rendered immediately on the first paint of SPA (non-SSR) routers. This was decided because it's usually better to see something instead of a blank white/black screen while you wait.

Considerations

  • If we hold all painting on first render, then we force this blank state on everyone. Not cool
  • If you'd like to opt-in to this, how could we do that? Automatically? Or manually?

tannerlinsley avatar Jul 09 '24 01:07 tannerlinsley

Relevant code: https://github.com/TanStack/router/blob/37c28cadc5ff43f6d8fdec5e2256e5391b0b1366/packages/react-router/src/router.ts#L1704-L1706

tannerlinsley avatar Jul 09 '24 01:07 tannerlinsley

@tannerlinsley Thanks for the detailed answer.

More info about our approach:

  • I agree that is not ok to display an empty page while loading the data
    • This is why we display a placeholder directly in the index.html even before the js is loaded

This is the way I thought that the route segments will load:

  • If there is no pending component, all the loaders will be awaited and after that the components will be rendered
  • If there is a pending component at a route segment, the router will wait only for the parent loaders and then display the parent components and the pending component from the current segment
    • And then it will go forward until the next segment that has a pending component

Wouldn't this behaviour allow enough flexibility? To choose when to wait for everything or display some pieces earlly? In this case, it wouldn't be limited only to the root route.

cristi-lng avatar Jul 09 '24 22:07 cristi-lng

@tannerlinsley Thanks for the information on this.

I am currently experiencing the same while developing an SPA. My two cents:

  • I like the idea of beforeLoad to do exactly what it suggests: do all of this before rendering the route - this way I can be sure my component has what it needs
  • Changing the behavior of beforeLoad depending on where it's used (root vs sub-route / SPA vs SSR) could be confusing and the type of context properties inside the root route would need to change, since they could be not loaded yet
  • If I would like the user to see something before beforeLoad finished a possible long running request within the root route, I would not use beforeLoad but handle the request on a deeper component level with it's own loading animation. This way the rest of the page is not blocked from rendering

Thanks for all the work on these amazing packages!

Nils-Kolvenbach avatar Jul 13 '24 21:07 Nils-Kolvenbach

@Nils-Kolvenbach In this ticket I was specifically referring to the loader and not the beforeLoad.

In our tests the beforeLoad works as expected. At least in SPA mode. It waits until everything is ready an works in serial fashion.

Do you see something different here? If so, do you have an example?

cristi-lng avatar Jul 18 '24 13:07 cristi-lng

Ah, thanks for clarification! After an update to the latest version (1.45.2 -> 1.49.1) and the corresponding router plugin and devtools everything works as expected for me.

Nils-Kolvenbach avatar Aug 21 '24 11:08 Nils-Kolvenbach

I have updated the example app to the latest version of the router (1.51.0) and the issue is still there.

https://stackblitz.com/edit/tanstack-router-vkppeu?file=src%2Fmain.tsx

cristi-lng avatar Aug 27 '24 16:08 cristi-lng

@cristi-lng were you able to figure this out? I am seeing similar behaviour which is unexpected, IMO, based on what I've read in the docs, but perhaps it's intentional. I basically would like to not render any route content until all of the relevant loaders are complete.

edit – to clarify, I would expect this behaviour:

  1. Initial loading indicator "Loading..."
  2. Navbar + "Index Route!" is shown next (no "Loading index data...")

https://github.com/user-attachments/assets/fe428e09-b7c3-4d49-a975-a5a67999a8f2

Dakkers avatar Jul 18 '25 17:07 Dakkers

@Dakkers I haven’t tested this issue on the latest version of the router to see if it's still reproducible. Instead, I took a different approach to work around the problem. I wrapped the entire RouterProvider in a custom component and only rendered the provider after fetching the necessary data. This gives me full control over the rendering process.

You can find an example in my starter repo here: https://github.com/cristi-lng/react-csr-base/blob/main/src/app.tsx

cristi-lng avatar Jul 22 '25 22:07 cristi-lng

It' still an issue. With react-router it's possible to set a basic placeholder in index.html together with hydrateFallbackElement and get a smooth transition when all lazy loaded code parts and loader data resolved. With tanstack router it's a mess to get a nice transition.

codeart1st avatar Oct 07 '25 17:10 codeart1st