Prefetching and Nextjs Loading.tsx not in synch.
I'm facing a weird behavior where I believe the prefetch hasn't finished yet, but nextjs loading is done. Resulting in a blank page for a second before the content shows on my screen.
10 Replies
foreign-sapphireOP•11mo ago
query client:
page.tsx:
and I have a service that does some stuff to integrate with our setup before I call prefetch:
The issue is spoty doesn't always happen
but when it does, it's really noticible
wise-white•11mo ago
Hey there. Didn't dive too deep into your code but the first thing that stands out to me is that you are using shouldDehydrateQuery (https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#streaming-with-server-components) which in the docs is used for streaming server prefetch data to the client without blocking a suspense/loading boundary.
Is it possible that because of the query client setup that it is actually bypassing loading suspense boundary when prefetchQuery is running? If you need to keep that in for use with useSuspenseQuery later, you might have to separate that out at the client component level
TanStack | High Quality Open-Source Software for Web Developers
Headless, type-safe, powerful utilities for complex workflows like Data Management, Data Visualization, Charts, Tables, and UI Components.

foreign-sapphireOP•11mo ago
oh my bad I forgot to remove that. Thing is I tried the normal way without streaming, then did the streaming with useSuspenseQuery but the results were all the same, however I noticed that with my GQL setup, the gql queries weren't dehydrated, they weren't serialized
another thing worth noting, is that I get the results when I deploy on staging, but when I build and serve locally, it doesn't happen
the gql queries weren't dehydrated unless I put an await, despite the queryclient having the shouldDehydrateQuery setup
wise-white•11mo ago
Ya this is quite a fun one... as this gets deeper into the technical side we may need to loop in @TkDodo 🔮 but here is a couple things I'm thinking about (which he might be able to correct me on if I'm wrong):
1. You use
const queryClient = getQueryClient();
within the page component. I believe that you only need to use that setup once as the initial queryClient provider set up for the entire application: <QueryClientProvider client={queryClient}
. When you need the a new queryClient for a page such as this, you can just create a new one with specific settings for that hydration boundary as useQueryClient for useQuery, useSuspenseQuery, etc will look for the closest queryClient to it. Benefit of doing it this way is that you don't need to enable shouldDehyrateQuery for the entire application but rather just for that hydration boundary.
2. Related to #1 but you are starting a water fall of getting the server session and then all of the promise.all await's with the getQueryClient thrown in the middle. Not sure if related or bad practice but something to worth noting.
I'd probably recommend starting by creating a new QueryClient instance, removing the streaming dehydration options, and seeing if loading.tsx is behaving and what data is being loaded an which times
Hope I'm speaking correctly here and that this helps!foreign-sapphireOP•11mo ago
Okay so to make sure I understand correctly and we're on the same page.
1. is used in the pages that I perform prefetching in, and also in the provider that wraps the whole app.
2. regarding your first point. Are you saying that the query client sent to the provider shouldn't containt the dehydration config, and in all those pages that use prefetching I create a new client with the dyhration configs?
Also, to align on my file structure in this part of the codebase:
As you can see I only have loading on the layout level, tho I had loading in each route but it didn't matter.
If I understand your first point correctly tho however, I did have in a past iteration separate instance of query client in each page.tsx, but still same results.
wise-white•11mo ago
Ya it is definitely possible that what you are doing with your point #1 is fine but just following the SSR docs, and my own personal app which is using streaming alongside awaiting prefetches before rendering, I'd try to simplify it by doing the following (which you may already tried):
1. Use the queryClient
app/get-query-client.ts
only for the QueryClientProvider for the entire application.
2. In each page which uses a hydration boundry, create a new queryClient using const queryClient = new QueryClient();
(remove the shouldDehyrateQuery options for now unless you are tied into using useSuspenseQuery already and then just remove all the awaits for the prefetches and add the options)
3. Instead of using loading.tsx which I've had troubles, with in the past for debugging, use your own custom <Suspense> fallbacks which may be easier to debug *edit: at each level you are await a prefetch
Hopefully something may jump out at you when debuggin with these steps...foreign-sapphireOP•11mo ago
Okay, regarding the second point, interesting thing happens when I don't include the shouldDehydrate. when I log the dehydrated client, it's empty, no queries get dehydrated, and when I use the same settings, only rest queries get dehydrated.
I will try doing the custom suspense, I remember for some reason combining both loading and custom suspense, but still no dice
should the Suspense wrap the HydrationBoundry or the other way around?
wise-white•11mo ago
Ya the combo of GQL and REST is something I haven't had to deal recently either and can't give you an insight there.
I've never tried to log out dehyrdrated cache but rather check to make sure it was working by seeing if it ever tried to make a client side call after loading of the application so not sure what would be expected there.
Suspense could wrap components where promised calls are being made so my guess is that if you want to have one for checking the prefetches at the page.tsx level, i'd create a layout.tsx and put the Suspense around the children there. Definitely could be wrong on this one but I'd start there. Make sure to remove the loading.tsx as well and just have the suspense in the layouts for each dir with a page
Let me know what you find and good luck!
foreign-sapphireOP•11mo ago
Alright will try and let you know
Thanks a lot for taking time to reply!
wise-white•11mo ago
:tanner: