React client: TanStack React Query integration recommended
For React applications, the recommended way to call a tRPC API is using the TanStack React Query Integration. This integration provides typesafe API calls, caching, invalidation, and management of loading and error state.
Per-request abortOnUnmount override
Override the global `abortOnUnmount` setting on individual queries by passing `{ trpc: { abortOnUnmount: true } }` as the second argument to `useQuery()`. This allows fine-grained control over which procedures cancel on unmount.
abortOnUnmount configuration option
By default, tRPC does not cancel requests via React Query. To opt into automatic request cancellation on component unmount, provide the `abortOnUnmount` configuration option when creating the tRPC React client.
React Query only supports aborting queries
@tanstack/react-query only supports aborting queries, not mutations or subscriptions.
Global abortOnUnmount configuration
Set `abortOnUnmount: true` when calling `createTRPCReact<AppRouter>()` to enable request cancellation on unmount for all procedures globally.
createTRPCQueryUtils returns router-like object
createTRPCQueryUtils returns an object with all available queries from your routers. You navigate this object the same way as your trpc client object. When you reach a query, you get access to query helpers like ensureData().
createTRPCQueryUtils parameters
createTRPCQueryUtils takes an object with two required parameters: queryClient (a @tanstack/react-query QueryClient instance) and client (the trpc client object created with createTRPCReact().createClient()).
createTRPCQueryUtils helpers available
createTRPCQueryUtils provides the same set of helpers as useUtils, including queryOptions and infiniteQueryOptions. The difference is that you need to pass in the queryClient and client objects when using createTRPCQueryUtils.
Avoid using createTRPCQueryUtils in React Components
createTRPCQueryUtils should not be used in React Components. Instead, use useUtils, which is a React hook that implements useCallback and useQueryClient under the hood.
Access client directly from createTRPCQueryUtils
If you need access to the client directly, you can use the client object that you passed to createTRPCQueryUtils during creation.
createTRPCQueryUtils QueryClient per request pattern
When using createTRPCQueryUtils with Remix Run or SSR, create a new QueryClient for every request instead of re-using the same QueryClient. This prevents cross-request data leakage.
createTRPCQueryUtils vs useUtils
The difference between useUtils and createTRPCQueryUtils is that useUtils is a React hook that uses useQueryClient under the hood and works better within React Components. createTRPCQueryUtils does not use React hooks, making it suitable for use outside of React Components.
createTRPCQueryUtils purpose and use case
createTRPCQueryUtils is a function used to access query helpers outside of React Components, such as in react-router loaders. It provides access to helpers that manage cached data of queries executed via @trpc/react-query. The helpers are thin wrappers around @tanstack/react-query's queryClient methods.
createTRPCQueryUtils example with react-router loader
Example: Create a QueryClient and call createTRPCQueryUtils with queryClient and client. In a react-router loader, use clientUtils.post.all.ensureData() to fetch data if it doesn't exist in cache. Pass the fetched data to useQuery via initialData option in the React component.
Typesafe conditional queries using skipToken example
import React, { useState } from 'react';
import { skipToken } from '@tanstack/react-query';
import { trpc } from './utils/trpc';
export function MyComponent() {
const [name, setName] = useState<string | undefined>();
const result = trpc.getUserByName.useQuery(name ? { name: name } : skipToken);
return (
<div>{result.data?.name}</div>
);
}
This example shows how to conditionally disable a query by passing skipToken when the name state is undefined, enabling typesafe conditional query execution.
Disable queries with skipToken in useQuery
To prevent a query from executing, pass skipToken as the first argument to useQuery, useInfiniteQuery, or useSubscription. skipToken is imported from @tanstack/react-query.
Example: getQueryKey to check if query is fetching
```tsx
import { useIsFetching, useQueryClient } from '@tanstack/react-query';
import { getQueryKey } from '@trpc/react-query';
import { trpc } from './utils/trpc';
function MyComponent() {
const queryClient = useQueryClient();
const posts = trpc.post.list.useQuery();
const postListKey = getQueryKey(trpc.post.list, undefined, 'query');
const isFetching = useIsFetching({ queryKey: postListKey });
return null;
}
```
This example shows using getQueryKey to get a query key for a specific procedure, then using useIsFetching to check if that query is currently fetching.
getQueryKey with routers
When calling getQueryKey with a router, you pass only the router as an argument. This generates a query key for the entire router namespace.
Example: getMutationKey
```tsx
import { getMutationKey } from '@trpc/react-query';
import { trpc } from './utils/trpc';
const mutationKey = getMutationKey(trpc.user.create);
```
This example shows getting a mutation key for a mutation procedure.
Example: getQueryKey to set router query defaults
```tsx
import { useQueryClient } from '@tanstack/react-query';
import { getQueryKey } from '@trpc/react-query';
import { trpc } from './utils/trpc';
function MyComponent() {
const queryClient = useQueryClient();
const postKey = getQueryKey(trpc.post);
queryClient.setQueryDefaults(postKey, { staleTime: 30 * 60 * 1000 });
return null;
}
```
This example shows using getQueryKey with a router to set query defaults for all queries in that router namespace, setting a staleTime of 30 minutes.
getMutationKey helper for mutations
Similarly to getQueryKey, tRPC provides a getMutationKey helper for mutations. The underlying function is the same as getQueryKey, so getQueryKey can technically be used for mutations as well. The only difference is semantic - getMutationKey is used specifically for mutation keys.
getQueryKey with procedures
When calling getQueryKey with a procedure, you can pass: procedure (required), input as a DeepPartial of the procedure's input type (optional), and QueryType (optional, defaults to 'any').
getQueryKey query type parameter
The getQueryKey function accepts an optional QueryType parameter with three valid values: 'query' (for useQuery), 'infinite' (for useInfiniteQuery), or 'any' (matches all queries). The default value is 'any'. The 'any' type will match all queries in the cache only if the React Query method uses fuzzy matching.
getQueryKey helper for queries and routers
The getQueryKey helper accepts a tRPC procedure or router to generate the correct query key for use with TanStack React Query. For queries, it accepts a procedure, optional input (as a deep partial), and an optional query type. For routers, it accepts just the router. The function returns a TRPCQueryKey array.
Generic component using RouterLike and UtilsLike
Example generic component pattern:
```tsx
import type { MyRouterLike, MyRouterUtilsLike } from './factory';
type MyGenericComponentProps = {
route: MyRouterLike;
utils: MyRouterUtilsLike;
};
function MyGenericComponent(props: MyGenericComponentProps) {
const { route } = props;
const thing = route.listThings.useQuery({ filter: 'qwerty' });
const mutation = route.doThing.useMutation({
onSuccess() {
props.utils.listThings.invalidate();
},
});
function handleClick() {
mutation.mutate({ name: 'Thing 1' });
}
return null;
}
function MyPageComponent() {
const utils = trpc.useUtils();
return (
<MyGenericComponent
route={trpc.deep.route.things}
utils={utils.deep.route.things}
/>
);
}
```
Factory pattern type setup example
Example setting up types from a router factory:
```ts
import { z } from 'zod';
import { t, publicProcedure } from './trpc';
import { RouterLike, UtilsLike } from '@trpc/react-query/shared';
const Thing = z.object({ id: z.string(), name: z.string() });
const ThingRequest = z.object({ name: z.string() });
const ThingQuery = z.object({ filter: z.string().optional() });
export function createMyRouter() {
return t.router({
createThing: publicProcedure.input(ThingRequest).output(Thing).mutation(({ input }) => ({ id: '1', ...input })),
listThings: publicProcedure.input(ThingQuery).output(z.array(Thing)).query(() => []),
})
}
type MyRouterType = ReturnType<typeof createMyRouter>
export type MyRouterLike = RouterLike<MyRouterType>
export type MyRouterUtilsLike = UtilsLike<MyRouterType>
```
Type inference example with usePostById hook
Example custom hook using inferred input and query options:
```ts
import { ReactQueryOptions, RouterInputs, trpc } from './trpc';
type PostByIdOptions = ReactQueryOptions['post']['byId'];
type PostByIdInput = RouterInputs['post']['byId'];
function usePostById(input: PostByIdInput, options?: PostByIdOptions) {
return trpc.post.byId.useQuery(input, options);
}
```
Type inference example with usePostCreate hook
Example custom hook using inferred React Query options:
```ts
import {
trpc,
type ReactQueryOptions,
type RouterInputs,
type RouterOutputs,
} from './trpc';
type PostCreateOptions = ReactQueryOptions['post']['create'];
function usePostCreate(options?: PostCreateOptions) {
const utils = trpc.useUtils();
return trpc.post.create.useMutation({
...options,
onSuccess(post, variables, onMutateResult, context) {
utils.post.invalidate();
options?.onSuccess?.(post, variables, onMutateResult, context);
},
});
}
```
Generic component pattern with router factory types
When using router factories, you can create generic React components that accept `MyRouterLike` and `MyRouterUtilsLike` as props. These components can then use the passed router to call `.useQuery()` and `.useMutation()` hooks, and use the utils prop for operations like `.invalidate()`. This pattern allows the same component to work with different router instances from different factory calls.
RouterLike and UtilsLike types for factory patterns
The `@trpc/react-query/shared` module exports `RouterLike` and `UtilsLike` types that can be used to generate abstract types from a router factory. These allow you to create generic React components that accept a router instance and utils as props, enabling code sharing across multiple instances of a factory-created router. Define them as: `export type MyRouterLike = RouterLike<MyRouterType>` and `export type MyRouterUtilsLike = UtilsLike<MyRouterType>` where `MyRouterType` is the inferred type of your factory's return value.
Extract typed options for specific procedures
Once you have exported `ReactQueryOptions` from your router, you can access options for specific procedures using nested bracket notation. For example, `ReactQueryOptions['post']['create']` gives you the typed options for the `post.create` mutation, which can then be used as the options parameter type in custom hook functions.
inferReactQueryProcedureOptions helper for React hooks
The `inferReactQueryProcedureOptions` helper is exported from `@trpc/react-query` and allows you to infer the types of React Query options directly from your router. This enables creating custom hooks with properly typed options. Export it as a type alias (e.g., `export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;`) to use across your React application.
Vanilla tRPC client with React Query example
You can use vanilla tRPC with @tanstack/react-query without the tRPC integration package. Example: import { createTRPCClient, httpBatchLink } from '@trpc/client'; const trpc = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url: 'YOUR_API_URL' })] }); Then use with React Query: const { data } = useQuery({ queryKey: ['posts'], queryFn: () => trpc.post.list.query() });
Type safety between tRPC backend and React Query client
The tRPC React Query integration is type safe by default. The types you define in your tRPC backend automatically drive the types of your React Query client, providing type safety throughout your React app.
Query keys are auto-generated by tRPC
The tRPC React Query wrapper generates and manages query keys on your behalf, based on the procedure inputs you provide. You do not need to manually specify query keys like you would with vanilla React Query.
createTRPCReact usage example with useQuery and useMutation
The tRPC React integration uses createTRPCReact to create a typed instance. Components then call useQuery and useMutation hooks on the router instance. Example: import { createTRPCReact } from '@trpc/react-query'; export const trpc = createTRPCReact<AppRouter>(); Then in components: const helloQuery = trpc.hello.useQuery({ name: 'Bob' }); const goodbyeMutation = trpc.goodbye.useMutation();
getQueryKey utility for retrieving tRPC-calculated keys
If you need to retrieve the query key which tRPC calculates internally, you can use the getQueryKey utility.
Use useState for tRPC and QueryClient in provider to support SSR
Create the queryClient and trpcClient inside useState in the provider component rather than outside as module-level constants. This ensures that each SSR request gets a unique client instance. For client-side only applications, you can move them outside if desired.
tRPC React Query hooks usage example
import { trpc } from '../utils/trpc';
export default function IndexPage() {
const userQuery = trpc.getUser.useQuery({ id: 'id_bilbo' });
const userCreator = trpc.createUser.useMutation();
return (
<div>
<p>{userQuery.data?.name}</p>
<button onClick={() => userCreator.mutate({ name: 'Frodo' })}>
Create Frodo
</button>
</div>
);
}
Call tRPC queries and mutations in React components
Once tRPC is set up with React Query, call queries using trpc.procedureName.useQuery() and mutations using trpc.procedureName.useMutation(). Query hooks accept input as an argument. Mutation hooks return an object with a mutate method to trigger the mutation.
Complete React Query provider setup example
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import React, { useState } from 'react';
import { trpc } from './utils/trpc';
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
async headers() {
return {
authorization: getAuthCookie(),
};
},
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
</trpc.Provider>
);
}
Set up tRPC and React Query providers
Wrap your application in trpc.Provider with a tRPC client, and wrap that in QueryClientProvider with a React Query QueryClient. Both the queryClient and trpcClient should be created inside useState in the provider component to ensure each SSR request gets a unique client. If you already use React Query in your application, reuse your existing QueryClient and QueryClientProvider.
createTRPCReact example
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/router';
export const trpc = createTRPCReact<AppRouter>();
useSuspenseQuery example with byId procedure
Example: const [post, postQuery] = trpc.post.byId.useSuspenseQuery({ id: '1' }); This calls the byId procedure with an input object containing id and destructures the returned data and query object.
useSuspenseQueries example with multiple queries
Example: const [posts, postQueries] = trpc.useSuspenseQueries((t) => props.postIds.map((id) => t.post.byId({ id }))); This fetches multiple posts by mapping over an array of post IDs.
useSuspenseInfiniteQuery example with all procedure
Example: const [{ pages }, allPostsQuery] = trpc.post.all.useSuspenseInfiniteQuery({}, { getNextPageParam(lastPage) { return lastPage.nextCursor; }, initialCursor: '', }); This sets up infinite query pagination with a next cursor.
useSuspenseQuery returns data and query tuple
useSuspenseQuery returns a tuple in the form [data, query], making it easy to directly use your data and rename the variable to something descriptive.
usePrefetchInfiniteQuery for infinite query prefetching
usePrefetchInfiniteQuery can be used to prefetch infinite query data in a parent component before rendering a child component wrapped in Suspense. It accepts the same options as useSuspenseInfiniteQuery including getNextPageParam and initialCursor.
usePrefetchQuery for component-level prefetching
usePrefetchQuery can be used in a parent component to prefetch data before rendering a child component wrapped in Suspense. The prefetched data will be available when useSuspenseQuery is called in the child component.
Prefetching improves suspense query performance
The performance of suspense queries can be improved by prefetching the query data before the Suspense component is rendered. This pattern is sometimes called 'render-as-you-fetch'.
Suspense with automatic SSR in Next.js crashes on query failure
When using suspense with tRPC's automatic SSR in Next.js, the full page will crash on the server if a query fails, even if you have an ErrorBoundary component.
useSuspenseQueries equivalent to useQueries
useSuspenseQueries is the suspense equivalent of useQueries and returns a tuple [queries, queryArray] where queries contains the fetched data and queryArray contains the query objects.
useSuspenseInfiniteQuery returns pages and query tuple
useSuspenseInfiniteQuery returns a tuple in the form [{ pages }, query], where pages contains the paginated data and query contains methods like isFetching, isFetchingNextPage, fetchNextPage, and hasNextPage.
Route-level prefetching with server-side helpers
For route-level prefetching in Next.js, use the Server-Side Helpers to implement server-side prefetching. This ensures data is fetched before the page renders, enabling the render-as-you-fetch pattern.
createTRPCQueryUtils for route-level prefetching
createTRPCQueryUtils creates utility functions for prefetching data at the router level. It takes a QueryClient and tRPC client and returns utility methods like ensureData that can be used in route loaders.
useInfiniteQuery cursor input requirement
A procedure must accept a cursor input of any type (string, number, etc.) to expose the useInfiniteQuery hook. The cursor can be nullish.
useInfiniteQuery procedure example with cursor-based pagination
Example infinite query procedure using tRPC and Prisma cursor-based pagination. The procedure accepts limit (1-100, nullish), cursor (nullish), and optional direction input. It fetches limit+1 items, uses the extra item as the next cursor, and returns {items, nextCursor}. The cursor should correspond to a database field (example uses myCursor).
useInfiniteQuery hook usage
Call trpc.procedureName.useInfiniteQuery(inputValue, options). The options object supports getNextPageParam callback to extract the next cursor from the last page, and optional initialCursor to set a starting cursor.
getInfiniteData helper
The getInfiniteData() helper method retrieves currently cached data from an infinite query. Call it via utils.procedureName.getInfiniteData(inputValue) where inputValue matches the query's input.
setInfiniteData helper
The setInfiniteData() helper updates a query's cached data. Call it via utils.procedureName.setInfiniteData(inputValue, updater) where inputValue matches the query's input and updater is a function that receives the current data (with pages and pageParams) and returns updated data. If data is null, return {pages: [], pageParams: []}.