Help with Next.js 15 App Router + tRPC + Tanstack Query

So, recently, I started learning the Holy T3 Stack(coz I bought a macbook recently, and I was like - why not become a soy dev!). So, I learned T3 Stack basics, like tRPC(setting it up in express, a vanilla TS project, then querying and mutation concepts), after that went on to TanStack Query(got my mind blown, that why from last 1.5 years I didn't use it in my React/Next apps!). And I know enough TS to write projects. So, I started with my Todo project for learning ofc(NOT gonna put it in my resume!). So, there's this HydrateClient component, which was in app/page.tsx, so I have a doubt, that, why NOT have it in the root layout/layout.tsx(its purpose is to hydrate the client components that come under it after making a request, then why NOT in layout.tsx and why in the page.tsx?), and should I put it in every page.tsx instead of layout.tsx if I wanna Hydrate the client and NOT pass the server fetched data as a prop? Secondly, how do I use tanstack query and tanstack RPC together, like do I make every component a "client component"(which is very weird coz Next.js makes Server Components so handy!) and then use the initialData prop inside the useQuery to populate the initial data? Thirdly, if I use the initalData prop then will it give me the holy grail of SSR or I'll be stuck with the hellish domain of CSR?(Although, from what I read/watched on YT, they say it provides SSR and can be seen in the Page Source Response, my question is, can the spiders/web crawlers read and understand it, coz then only it'll benefit the SEO!) May React Gods bless all of us!
9 Replies
Bartosz Grabias
Bartosz Grabias4mo ago
For the first question The reason is that leyout is not re-rendered on navigation. You have to have the hydrate client in the page to have it be "re-rendered", so that it can get the new data. Second question Depends on if you need the data to update after page load. If not - you can use the serverside variants and get the data on server. If yes then you can use prefetching & hydrate client, though that for me never resulted in proper SSR and I didn't have the time to investigate. Otherwise as you suggested - get the data on sever via the server side trpc client, then pass it as the initial data. Remeber to set the invalidation period, otherwise the client with immediately re-request the data on render. Third question Yes, if you pass initiial data it will render on the server and deliver full HTML to the client. Though beware of time-dependent data (if you have things such as x seconds ago, current time, etc.) as that will cause hydration errors.
Rajneesh Mishra
Rajneesh MishraOP4mo ago
Thank you very very much!!!
faccia
faccia4mo ago
I was confused about the hydrate client as well and if I understand correctly, you're saying that the hydrate client will not work as intended if I put it in the layout that I have to wrap each page.tsx with the hydrate client component? Am I correct about that
Rajneesh Mishra
Rajneesh MishraOP4mo ago
Yup HydrateClient is a way to simply get the data fetched by trpc in a server component without passing it around as a prop into the client component. As simple as that. It also maintains cache of requests, so if you keep it at layout level, then the global cache for tRPC will be affected!! That's the main problem.... Or you can go with the initialData approach where you prefetch using tRPC in a server component and then pass it down as initialData and receive in the client component and populate the useQuery hook's initialData prop with that. It'll give you SSR.
faccia
faccia4mo ago
so every page.tsx component has to be wrapped in the hydrate client, especially if I'm making api.user.get() or orsomething like that in it
Rajneesh Mishra
Rajneesh MishraOP4mo ago
Yup, unless you wanna go down the initialData passing as a prop route and drill the prop outta that app!
Bartosz Grabias
Bartosz Grabias4mo ago
Well, only if you are using prefetching (api.user.prefetch() IIRC), otherwise I don't think it helps in any way
faccia
faccia4mo ago
right but if you are using prefetch do you await the prefetch?
void.foo.prefetch() || await api.foo.prefetch()
void.foo.prefetch() || await api.foo.prefetch()
and than when you use a useQuery do you use useQuery or useSuspenseQuery and if you do use useSupenseQuery do you have to use the Suspence
<HydrateClient>
<Suspense
fallback={<div>Loading
form…</div>}
>
<EditProfileForm />
</Suspense>
</HydrateClient>
<HydrateClient>
<Suspense
fallback={<div>Loading
form…</div>}
>
<EditProfileForm />
</Suspense>
</HydrateClient>
really having a time trying to figure what to use when
Bartosz Grabias
Bartosz Grabias4mo ago
I'm not 100% on the details of how this works in all cases. In you don't await the prefetch, the SSR will complete without data, initial client render will be without data, but then it will load the data from the hydrate client, not via API call (i.e. the data is streamed) If you await it, i think it's supposed to have the data the first time useQuery is called, so it should SSR with data. With suspense it always SSRed in the no-data state. I didn't dive into details, but I would expect it to stream the loaded chunk afterwards, i.e. without extra API calls.

Did you find this page helpful?