Replies: 1 comment
-
|
The model you want is the experimental streamed hydration path, not the route-level Wrap the app in <QueryClientProvider client={queryClient}>
<ReactQueryStreamedHydration>{children}</ReactQueryStreamedHydration>
</QueryClientProvider>The important boundary is the query function: because it lives in a client component module, don't import DB/server-only code there. If the query needs a server-only procedure, expose that through a route handler/server function/RPC endpoint, or keep a small Server Component prefetch wrapper at the subtree boundary. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Initially, I was tempted to title this discussion “TanStack Query’s SSR data-fetching model was not made to work with Next.js.” But that felt a little harsh without a proper discussion or without fully knowing if I am missing something or not.
There seems to be a mismatch between React Query’s recommended SSR model for Next.js and the granular, component-owned data-fetching model encouraged by the Next.js App Router.
Unlike some other frameworks, Next.js really discourages you to fetch all of the data you need for a given page. It seems that the framework would much rather have you fetch all of the data that each subtree or component needs for itself, instead of having all the data fetching be something owned by the route itself.
Next.js encourages fetching data in the component or subtree that consumes it. Identical
fetchrequests are memoized, while Suspense boundaries can be placed close to slow dynamic regions so the static shell renders immediately.Consider an order-details page with a static layout and several independently loading regions:
Each dynamic component needs the same order object:
The desired behavior is for the first subscriber to start the query during server rendering.
The prefetch model
The currently recommended React Query SSR pattern is for a Server Component to prefetch the query and render a
HydrationBoundary:This works, including streaming pending queries in recent versions, but route-level prefetching (higher order RSC like described above) has an important consequence in Next.js: the request-time data access happens in a single RSC parent, outside the granular Suspense boundaries that describe the page’s loading behavior.
That defeats the purpose of structuring the page like this:
The Suspense boundaries suggest that the static layout and each dynamic region can be rendered independently. However, a route-level prefetch moves the actual request-time dependency above all those boundaries.
You could then say, (and this is the best I could come up with so far) that we could move the prefetching down into each subtree:
The page could then render it under Suspense:
This preserves the granularity expected by Next.js, but it feels like the wrong abstraction.
Every dynamic region now requires a Server Component whose only job is to prefetch, coupled with a hydration boundary colocated with that prefetch. Placing multiple Hydration boundaries like that feels quite wrong.
This is where the two models appear incompatible:
Neither option produces the component-owned model shown in the original example, where a component calls
useSuspenseQuery, the first subscriber starts the server query, sibling subscribers share it, and the surrounding Suspense boundaries remain the only loading architecture.Beta Was this translation helpful? Give feedback.
All reactions