new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

tRPC · all subjects

client/tanstack-react-query

37 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Create tRPC caller for server components

Create trpc/server.tsx with import 'server-only' to ensure the file cannot be imported from the client. Use createTRPCOptionsProxy with the router and a cached getQueryClient function to create a stable proxy. Wrap getQueryClient with React's cache() to return the same client during the same request.

Prefetch queries in server components

Prefetch queries in server components using queryClient.prefetchQuery() with trpc.queryOptions(). This initiates the query on the server without suspending until data is needed, leveraging Next.js App Router's streaming capabilities. Optionally await the prefetchQuery call to ensure data is available on first render, with the tradeoff of slower page load since the server must complete the query before sending HTML.

React Server Components tRPC setup overview

tRPC can be used with React Server Components (RSC) frameworks like Next.js App Router, but RSC itself solves many of the same problems tRPC was designed to solve, so tRPC may not be necessary. There is no one-size-fits-all way to integrate tRPC with RSCs; treat the guide as a starting point and adjust to your needs.

Install dependencies for tRPC with React Server Components

To set up tRPC with React Server Components, install: @trpc/server, @trpc/client, @trpc/tanstack-react-query, @tanstack/react-query@latest, zod, client-only, and server-only.

Create tRPC context using React cache

Initialize tRPC context in trpc/init.ts using the initTRPC function and wrap the context creation with React's cache() to ensure stable context during server rendering. This prevents context from being recreated on each request.

QueryClient configuration for RSC

When creating a QueryClient for RSC, set staleTime to 30 seconds or higher to avoid refetching immediately on the client. Extend defaultShouldDehydrateQuery to include pending queries to support hydrating promises over the network. Optionally configure serializeData and deserializeData if using a data transformer.

Create tRPC client for client components

In trpc/client.tsx, import only the type definition of your tRPC router and use createTRPCContext to create typesafe hooks. Export the context provider from this file. Use 'use client' directive. Maintain a singleton QueryClient in the browser using getQueryClient function to avoid recreating it during React suspensions.

TRPCReactProvider setup for RSC

Mount TRPCReactProvider in your root layout (e.g., app/layout.tsx). Avoid using useState when initializing the query client if you don't have a suspense boundary between the provider and code that may suspend, as React will discard the client on initial render if it suspends without a boundary.

Use HydrationBoundary to dehydrate query client

Wrap client components with HydrationBoundary and pass dehydrate(queryClient) as the state to share prefetched data from the server to the client.

useQuery vs useSuspenseQuery in client components

In client components, use useQuery for gradual loading where greeting.data may initially be undefined before data streams in. Use useSuspenseQuery when handling loading and error states with Suspense and Error Boundaries.

Get data in server components without query client

To access data in a server component, create a server caller using appRouter.createCaller(createTRPCContext) and call procedures directly. This method is detached from the query client and does not store data in the cache, so data obtained this way cannot be expected to be available in client components.

Share data between server and client components

If you need to use data both on the server and in client components and understand the tradeoffs documented in React Query's Advanced Server Rendering guide, use queryClient.fetchQuery() instead of prefetchQuery() to have the data available on both server and client.

Helper functions for prefetch and HydrateClient

Create prefetch and HydrateClient helper functions in trpc/server.tsx to make prefetching more concise and reusable. The prefetch function checks if the query is infinite and calls the appropriate prefetch method. HydrateClient is a wrapper around HydrationBoundary with getQueryClient() baked in.

RSC trpc/init.ts example

```ts import { initTRPC } from '@trpc/server'; import { cache } from 'react'; export const createTRPCContext = cache(async () => { return { userId: 'user_123' }; }); const t = initTRPC.create(); export const createTRPCRouter = t.router; export const createCallerFactory = t.createCallerFactory; export const baseProcedure = t.procedure; ``` This example initializes tRPC for RSC with React's cache for stable context creation.

RSC trpc/query-client.ts example

```ts import { defaultShouldDehydrateQuery, QueryClient, } from '@tanstack/react-query'; import superjson from 'superjson'; export function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { staleTime: 30 * 1000, }, dehydrate: { // serializeData: superjson.serialize, shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === 'pending', }, hydrate: { // deserializeData: superjson.deserialize, }, }, }); } ``` This creates a QueryClient configured for RSC with 30-second stale time and pending query dehydration.

RSC trpc/client.tsx example

```tsx 'use client'; import type { QueryClient } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query'; import { createTRPCClient, httpBatchLink } from '@trpc/client'; import { createTRPCContext } from '@trpc/tanstack-react-query'; import { useState } from 'react'; import { makeQueryClient } from './query-client'; import type { AppRouter } from './routers/_app'; export const { TRPCProvider, useTRPC } = createTRPCContext<AppRouter>(); let browserQueryClient: QueryClient; function getQueryClient() { if (typeof window === 'undefined') { return makeQueryClient(); } if (!browserQueryClient) browserQueryClient = makeQueryClient(); return browserQueryClient; } function getUrl() { const base = (() => { if (typeof window !== 'undefined') return ''; if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; return 'http://localhost:3000'; })(); return `${base}/api/trpc`; } export function TRPCReactProvider( props: Readonly<{ children: React.ReactNode; }>, ) { const queryClient = getQueryClient(); const [trpcClient] = useState(() => createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: getUrl(), }), ], }), ); return ( <QueryClientProvider client={queryClient}> <TRPCProvider trpcClient={trpcClient} queryClient={queryClient}> {props.children} </TRPCProvider> </QueryClientProvider> ); } ``` This creates the tRPC client provider for client components with singleton query client management.

RSC trpc/server.tsx example

```tsx import 'server-only'; import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query'; import { createTRPCClient, httpLink } from '@trpc/client'; import { cache } from 'react'; import { createTRPCContext } from './init'; import { makeQueryClient } from './query-client'; import { appRouter } from './routers/_app'; import type { AppRouter } from './routers/_app'; export const getQueryClient = cache(makeQueryClient); export const trpc = createTRPCOptionsProxy({ ctx: createTRPCContext, router: appRouter, queryClient: getQueryClient, }); ``` This creates the server-side tRPC proxy for prefetching in server components.

RSC prefetch in server component example

```tsx import { dehydrate, HydrationBoundary } from '@tanstack/react-query'; import { getQueryClient, trpc } from '../trpc/server'; import { ClientGreeting } from './client-greeting'; export default async function Home() { const queryClient = getQueryClient(); void queryClient.prefetchQuery( trpc.hello.queryOptions({ /** input */ }), ); return ( <HydrationBoundary state={dehydrate(queryClient)}> <div>...</div> <ClientGreeting /> </HydrationBoundary> ); } ``` This example prefetches a query in a server component without awaiting, allowing streaming.

RSC client component with useQuery example

```tsx 'use client'; import { useQuery } from '@tanstack/react-query'; import { useTRPC } from '../trpc/client'; export function ClientGreeting() { const trpc = useTRPC(); const greeting = useQuery(trpc.hello.queryOptions({ text: 'world' })); if (!greeting.data) return <div>Loading...</div>; return <div>{greeting.data.greeting}</div>; } ``` This client component uses useQuery to consume prefetched data from the server.

RSC client component with useSuspenseQuery example

```tsx 'use client'; import { useSuspenseQuery } from '@tanstack/react-query'; import { useTRPC } from '../trpc/client'; export function ClientGreeting() { const trpc = useTRPC(); const { data } = useSuspenseQuery(trpc.hello.queryOptions()); return <div>{data.greeting}</div>; } ``` This client component uses useSuspenseQuery for Suspense-based loading state handling.

RSC caller for server component data access example

```tsx import { createTRPCContext } from './init'; import { appRouter } from './routers/_app'; export const caller = appRouter.createCaller(createTRPCContext); ``` Then in a server component: ```tsx import { caller } from '../trpc/server'; export default async function Home() { const greeting = await caller.hello(); return <div>{greeting.greeting}</div>; } ``` This creates and uses a server caller to directly invoke procedures without query client.

RSC fetchQuery for server and client data sharing example

```tsx import { getQueryClient, HydrateClient, trpc } from '../trpc/server'; import { ClientGreeting } from './client-greeting'; export default async function Home() { const queryClient = getQueryClient(); const greeting = await queryClient.fetchQuery(trpc.hello.queryOptions()); // Do something with greeting on the server return ( <HydrateClient> <div>...</div> <ClientGreeting /> </HydrateClient> ); } ``` This awaits fetchQuery to have data available on both server and client, with slower page load.

HydrateClient and prefetch helper functions example

```tsx export function HydrateClient(props: { children: React.ReactNode }) { const queryClient = getQueryClient(); return ( <HydrationBoundary state={dehydrate(queryClient)}> {props.children} </HydrationBoundary> ); } export function prefetch<T extends ReturnType<TRPCQueryOptions<any>>>( queryOptions: T, ) { const queryClient = getQueryClient(); if (queryOptions.queryKey[1]?.type === 'infinite') { void queryClient.prefetchInfiniteQuery(queryOptions as any); } else { void queryClient.prefetchQuery(queryOptions); } } ``` These helpers simplify prefetching and hydration in server components.

RSC with Suspense and Error Boundary example

```tsx import { HydrateClient, prefetch, trpc } from '../trpc/server'; import { Suspense } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { ClientGreeting } from './client-greeting'; export default async function Home() { prefetch(trpc.hello.queryOptions()); return ( <HydrateClient> <div>...</div> <ErrorBoundary fallback={<div>Something went wrong</div>}> <Suspense fallback={<div>Loading...</div>}> <ClientGreeting /> </Suspense> </ErrorBoundary> </HydrateClient> ); } ``` This example combines prefetch, HydrateClient, Suspense, and Error Boundary.

React Query v5 migration

React Query has been updated to v5, which is now required as a peer dependency. The main change is replacing isLoading with isPending. Refer to the TanStack React Query v5 migration guide at https://tanstack.com/query/v5/docs/framework/react/guides/migrating-to-v5.

Bi-directional infinite queries

tRPC v11 adds support for bi-directional infinite queries in useInfiniteQuery().

useSuspenseQueries hook

tRPC v11 introduces useSuspenseQueries() hook for React Suspense support with multiple queries.

New TanStack React Query integration

tRPC v11 introduces a new TanStack React Query integration available as a non-breaking addition. This provides better compatibility with React Query v5.

useSuspenseInfiniteQuery hook usage

Call useSuspenseInfiniteQuery on a procedure with input parameters as the first argument and infinite query options as the second argument. The options object can include getNextPageParam function to extract the next cursor and initialCursor to set the starting cursor value. The hook returns an array where the first element is an object with a 'pages' property containing the paginated data.

New TanStack React Query integration overview

tRPC's new TanStack React Query integration is available on the `next` release. It is simpler and more TanStack Query-native than the classic React Query integration. It utilizes the QueryOptions and MutationOptions interfaces native to TanStack React Query instead of wrapping useQuery and useMutation with tRPC's own client.

TanStack React Query integration uses queryOptions pattern

With the new TanStack React Query integration, instead of using a custom tRPC hook, you use useTRPC to get the trpc client and then call trpc.procedureName.queryOptions() to get native TanStack React Query QueryOptions. These are passed to useQuery from @tanstack/react-query. Example: const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));

Benefits of new TanStack React Query integration

The new TanStack React Query integration provides simplicity by removing a layer of abstraction, familiarity for those already using TanStack Query, more idiomatic React patterns that work with the React Compiler, better maintainability by using native interfaces, and positive community feedback. The classic React Query integration broke React hook rules and could not be correctly linted.

Classic React Query integration will continue to be maintained

The classic tRPC React Query integration will continue to be maintained for a long time but will not receive significant new features and is considered stable. Both the classic and new TanStack React Query clients are compatible with each other and can exist in the same application.

Migration from classic to new TanStack React Query integration

New projects should start with the new TanStack React Query integration. Existing projects can migrate gradually. Both clients are compatible and can coexist in the same application, allowing migration at your own pace. A codemod is being worked on to assist with migration, with community contributions welcomed.

@trpc/tanstack-react-query beta status

The @trpc/tanstack-react-query package is currently in beta and may introduce breaking changes without respecting semver while the API is being stabilized.

@trpc/tanstack-react-query minimum React Query version

@trpc/tanstack-react-query requires @tanstack/react-query v5.62.8 or higher.

AI coding agent skills installation for tRPC

AI coding agents can install tRPC skills for better code generation by running npx @tanstack/intent@latest install.

Give your agent this brain