Best way to prefetch queries with tRPC and app/ dir

I am in the process of switching my pages/ T3 app to the new fancy app directory and know that React Query has a few ways to prefetch queries, and was wondering which one is the best for use with T3 app dir. 1. initialData The easiest, but also the most annoying in my eyes. You fetch the data on the server and then pass it down as initialData. 2. prefetchQuery & HydrationLayer Saw this in the docs but i’m not sure if it will work with the extended version of react-query that tRPC uses. 3. @tanstack/react-query-next-experimental This seems to be brand new, and I can’t really tell what’s different from the second option. Which method is best for prefetching on the server with T3 app dir?
Solution:
I think the main takeaway is: 1. prefetch the data using .prefetch() 2. dehydrate the state 3. pass the dehydrated state into react query 4. call .query(), which will pull from the cache instead of re-fetching...
Jump to solution
7 Replies
Endgame1013
Endgame10138mo ago
I’ve used a setup like this before and it’s worked pretty well:
// MySSRComponent.tsx
import { createSSRHelper } from '@/server/api/ssr';
import ReactQueryHydrate from '@/components/ReactQueryHydrate';
import { dehydrate } from '@tanstack/react-query';

export default async function MySSRComponent() {
// 1. create the server-side helper
const helpers = await createSSRHelper();
// 2. pre-fetch the tRPC procedure server-side
await helpers.example.greeting.prefetch();
// 3. get the dehydrated query client and pass it down to react-query's context provider
const dehydratedState = dehydrate(helpers.queryClient);

return (
<ReactQueryHydrate state={dehydratedState}>
<MyClientComponent />
</ReactQueryHydrate>
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MyClientComponent.tsx
'use client';

import { api } from '@/lib/api';

export default function MyClientComponent() {
const { data, status } = api.example.greeting.useQuery();

if (status !== 'success') {
// won't happen since the query has been prefetched
return <div>Loading...</div>;
}

return (
<pre>{JSON.stringify(data, null, 4)}</pre>
);
}
// MySSRComponent.tsx
import { createSSRHelper } from '@/server/api/ssr';
import ReactQueryHydrate from '@/components/ReactQueryHydrate';
import { dehydrate } from '@tanstack/react-query';

export default async function MySSRComponent() {
// 1. create the server-side helper
const helpers = await createSSRHelper();
// 2. pre-fetch the tRPC procedure server-side
await helpers.example.greeting.prefetch();
// 3. get the dehydrated query client and pass it down to react-query's context provider
const dehydratedState = dehydrate(helpers.queryClient);

return (
<ReactQueryHydrate state={dehydratedState}>
<MyClientComponent />
</ReactQueryHydrate>
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MyClientComponent.tsx
'use client';

import { api } from '@/lib/api';

export default function MyClientComponent() {
const { data, status } = api.example.greeting.useQuery();

if (status !== 'success') {
// won't happen since the query has been prefetched
return <div>Loading...</div>;
}

return (
<pre>{JSON.stringify(data, null, 4)}</pre>
);
}
ethan
ethan8mo ago
what does the ReactQueryHydrate component look like or is it just a client wrapper
Endgame1013
Endgame10138mo ago
'use client';

import { Hydrate as RQHydrate, HydrateProps } from '@tanstack/react-query';

export default function ReactQueryHydrate(props: HydrateProps) {
return <RQHydrate {...props} />;
}
'use client';

import { Hydrate as RQHydrate, HydrateProps } from '@tanstack/react-query';

export default function ReactQueryHydrate(props: HydrateProps) {
return <RQHydrate {...props} />;
}
Solution
Endgame1013
Endgame10138mo ago
I think the main takeaway is: 1. prefetch the data using .prefetch() 2. dehydrate the state 3. pass the dehydrated state into react query 4. call .query(), which will pull from the cache instead of re-fetching
ethan
ethan8mo ago
yeah, makes sense thank you!!
yaba101
yaba1017mo ago
@Endgame1013 can we have repo for this, because I don't get the import { createSSRHelper } from '@/server/api/ssr'; implementation
Endgame1013
Endgame10137mo ago
@yaba101, here is the code for that portion, sorry for the oversight:
import { appRouter } from './routes/_app';
import { createContext } from './context';
import { createServerSideHelpers } from '@trpc/react-query/server';
import superjson from 'superjson';

/**
* SSR/RSC caller
* @description use this function to create a TRPC server-side caller
* @see https://trpc.io/docs/server/server-side-calls#create-caller
*/
export const createSSRCaller = async () => {
return appRouter.createCaller(await createContext());
};

/**
* Server-Side Helper
* @description use this function to call tRPC procedures server-side and hydrate `react-query`'s cache
* @see https://trpc.io/docs/client/nextjs/server-side-helpers#1-internal-router
*/
export const createSSRHelper = async () =>
createServerSideHelpers({
router: appRouter,
ctx: await createContext(),
transformer: superjson,
});
import { appRouter } from './routes/_app';
import { createContext } from './context';
import { createServerSideHelpers } from '@trpc/react-query/server';
import superjson from 'superjson';

/**
* SSR/RSC caller
* @description use this function to create a TRPC server-side caller
* @see https://trpc.io/docs/server/server-side-calls#create-caller
*/
export const createSSRCaller = async () => {
return appRouter.createCaller(await createContext());
};

/**
* Server-Side Helper
* @description use this function to call tRPC procedures server-side and hydrate `react-query`'s cache
* @see https://trpc.io/docs/client/nextjs/server-side-helpers#1-internal-router
*/
export const createSSRHelper = async () =>
createServerSideHelpers({
router: appRouter,
ctx: await createContext(),
transformer: superjson,
});
Want results from more Discord servers?
Add your server
More Posts
onSuccess, onError and onSettled deprecated next major version?with `onSuccess`, `onError` and `onSettled` being deprecated soon™️, what would the best practice beHow did Vercel Implement this ??When you encounter an error, it opens up the editor file in VSCode. How do you built this browser feHave someone implemented pagination with server actions and prisma?Could you send link if soRestrict direct access to NextJS routesI am writing a user onboarding flow where the whole onboarding flow is about 3 pages, it starts withShould I use "use server" directive for database queries```ts "use server" import { db } from "~/server/db"; export async function getArticle(id: string) Is there a way to refactor the query/mutation function to a different file?Basically I'm wondering if it's possible to get the type of the function that's passed into the querI'm a complete React beginner, is this the correct approach for Components?Hello everyone, I am tasked to do some buttons that have both a logo, and text. The logo is supposedHow to keep track of multiple running mutations with same function but different input variables?I have a single mutate function that's basically: const doWork = api.example.doWork.useMutation() Vercel postgres + Prisma migrateI got a super simple setup using Vercel's Postgres services, when running `prisma migrate dev` I do How to simulate Open AI's word by word stream works in the backend?I have a completed string and i want to deliver it word by word instead of dumping the whole string.