Tanstack Start, React Query, deferred loading, prefetch, streaming
I have been trying to achieve deferred loading for hours now and I cannot make it work.
Basically I have /dashboard/* routes that are only accessible to authenticated users.
So in my dashboard/route.tsx layout, I prefetch in the loader without awaiting and useSuspenseQuery in the layout.
Even this simple setup doesn't work. When I look at the network tab I can see a client call the /me trpc
What I am trying to achieve is showing a loading while the query resolves.
The documentation mention NextJS but not Tanstack Start.
For example, on some NextJS documentation they say you need shouldDehydrateQuery.
Do I need it for Tanstack Start ?
21 Replies
passive-yellow•5mo ago
take a look at this example https://github.com/TanStack/router/blob/af455d812f220a6df3d43dbe1610cf0d173ea092/e2e/react-start/basic-react-query/src/router.tsx
the react query plugin handles configuring the hydration / dehydration parts
GitHub
router/e2e/react-start/basic-react-query/src/router.tsx at af455d81...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
passive-yellow•5mo ago
also just a note that loaders run on the client as well so when its working you shouldn't see the network call only for the initial page load
variable-limeOP•5mo ago
Thanks ! This is so confusing, there is a lot of examples online and they are all different
for example, your link shows
const queryClient = new QueryClient()
inside of createRouter.
Isn't createRouter only called once when the server starts ?
Does that mean every requests will share the same query client ? On the NextJS doc they say you need to create a new query client every request to avoid sharing cache between userspassive-yellow•5mo ago
I'm not an expert so take my input with skepticism but
createRouter
gets called on the server during SSR for each incoming request and also on the client.
So the life of the context and query client on the server is just the length of the request and it moves to the client where the router and query cache are hydrated from the server.
I was also a little bit confused at first as there's a lot of documentation around the topic with subtle differences depending on the framework.variable-limeOP•5mo ago
Should I see a network request immediately after the page loads ?
I just double-checked and you are right, createRouter gets called on every requests
passive-yellow•5mo ago
It depends on whether or not you await the fetch / prefetch and if it's a server load or client transition
so if you await a prefetch in a route and you load that page directly, you shouldn't see the network call
but if you navigate to it from another page you will since the loader will run on the client in that case
maybe the await doesn't matter honestly it's hard to keep track of all these things
variable-limeOP•5mo ago
My query takes 4 seconds to resolve.
I don't await the prefetchQuery.
The page takes 4 seconds to load.
I see another client request after the page loads that takes another 4 seconds
passive-yellow•5mo ago
I think it shouldn't matter because of the streaming the fetch should still happen on the server
if the loader ran on the server
variable-limeOP•5mo ago
yes it's loading fine but it not very efficient
I tried staleTime: Infinity but still, the client refetch
passive-yellow•5mo ago
I'll share some relevant parts of my app where it's working as expected (confirmed await doesn't matter since it streams)
variable-limeOP•5mo ago
Ok thank you for your help
So, I had a defaultoptions StaleTime: 0 in the createRouter and staleTime: Infinity in my layout. I thought it would override the default but apparently not
so now it's working, the client doesn't refetch on load
passive-yellow•5mo ago
So in this case if I load the home page directly ie refresh I don't see a network call to the upcoming meetings query
passive-yellow•5mo ago
Oh nice. From what I could tell staleTime on the server didn't seem to have an effect on the query when it made it to the client
variable-limeOP•5mo ago
ohhh
they don't share default options maybe
that's why
passive-yellow•5mo ago
I don't set any staletime on my query client or server query though and it is working as expected
I'm loving the developer experience with the router and query. Super nice to be able to drill down into all your data fetching and routing stuff
variable-limeOP•5mo ago
Yes it's really cool
So now that my setup is working, time to experiement with loader. I read that if you await prefetch, the page load pauses until the prefetch is done.
And if you don't await prefetch, you can show a loader while the promise resolves
with <Suspense> or <Await>
I'll try
passive-yellow•5mo ago
Yep that should be how it works
Good luck !
variable-limeOP•5mo ago
Thanks so much for your help man
variable-limeOP•5mo ago
Ok, awaiting or not the prefetchquery doesn't change anything, the page pauses until the loader is finished.
The example here https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading#deferred-data-loading-with-external-libraries doesn't work for me
Deferred Data Loading | TanStack Router React Docs
TanStack Router is designed to run loaders in parallel and wait for all of them to resolve before rendering the next route. This is great most of the time, but occasionally, you may want to show the u...
variable-limeOP•5mo ago
Not awaiting prefetch and wrapping in suspense doesn't show the fallback
Maybe because I am in a layout route (dashboard.route.tsx) and not in a page route...
passive-yellow•5mo ago
Weird, I don't think that should matter tbh