React Query Integration (Classic, deprecated)
The classic React Query integration, which only gets bug fixes and will be removed in the next major version of tRPC
On this page
tRPC offers a first class integration with React. Under the hood this is simply a wrapper around the very popular @tanstack/react-query, so we recommend that you familiarize yourself with React Query, as their docs go into much greater depth on its usage.
If you are using Next.js we recommend using our integration with that instead.
❓ Do I have to use an integration?
No! The integration is fully optional. You can use @tanstack/react-query using just a vanilla tRPC client, although then you'll have to manually manage query keys and do not get the same level of DX as when using the integration package.
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpBatchLink<TRouter extends AnyRouter>(opts: HTTPBatchLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpBatchLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>> & DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>
AppRouter } from '../server';
export const const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>): TRPCClient<...>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>> & DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>[]
links: [httpBatchLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>(opts: HTTPBatchLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<...>
httpBatchLink({ url: string | URLurl: 'YOUR_API_URL' })],
});import { function useQuery<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = readonly unknown[]>(options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): DefinedUseQueryResult<TData, TError> (+2 overloads)useQuery } from '@tanstack/react-query';
import { const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc } from '../utils/trpc';
function function PostList(): voidPostList() {
const { const data: {
id: number;
title: string;
}[] | undefined
data } = useQuery<{
id: number;
title: string;
}[], Error, {
id: number;
title: string;
}[], string[]>(options: UndefinedInitialDataOptions<{
id: number;
title: string;
}[], Error, {
id: number;
title: string;
}[], string[]>, queryClient?: QueryClient): UseQueryResult<{
id: number;
title: string;
}[], Error> (+2 overloads)
useQuery({
queryKey: string[]queryKey: ['posts'],
queryFn?: unique symbol | QueryFunction<{
id: number;
title: string;
}[], string[], never> | undefined
queryFn: () => const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc.post: DecoratedProcedureRecord<{
transformer: false;
errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
list: QueryProcedure<{
input: void;
output: {
id: number;
title: string;
}[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.list: {
query: Resolver<{
input: void;
output: {
id: number;
title: string;
}[];
errorShape: DefaultErrorShape;
transformer: false;
}>;
}
list.query: (input: void, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<{
id: number;
title: string;
}[], DefaultErrorShape>
query(),
});
const data: {
id: number;
title: string;
}[] | undefined
data; // Post[]
// ...
}The tRPC React Query Integration
This library enables usage directly within React components.
import { const trpc: CreateTRPCReactBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: {
name: string;
};
output: {
greeting: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
goodbye: MutationProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, unknown> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>
trpc } from '../utils/trpc';
export default function function IndexPage(): JSX.ElementIndexPage() {
const const helloQuery: UseTRPCQueryResult<{
greeting: string;
}, TRPCClientErrorLike<{
input: {
name: string;
};
output: {
greeting: string;
};
transformer: false;
errorShape: DefaultErrorShape;
}>>
helloQuery = const trpc: CreateTRPCReactBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: {
name: string;
};
output: {
greeting: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
goodbye: MutationProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, unknown> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>
trpc.hello: DecoratedQuery<{
input: {
name: string;
};
output: {
greeting: string;
};
transformer: false;
errorShape: DefaultErrorShape;
}>
hello.useQuery: ProcedureUseQuery
<{
greeting: string;
}, {
greeting: string;
}>(input: {
name: string;
} | unique symbol, opts?: UseTRPCQueryOptions<{
greeting: string;
}, {
greeting: string;
}, TRPCClientErrorLike<{
input: {
name: string;
};
output: {
greeting: string;
};
transformer: false;
errorShape: DefaultErrorShape;
}>, {
greeting: string;
}> | undefined) => UseTRPCQueryResult<{
greeting: string;
}, TRPCClientErrorLike<{
input: {
name: string;
};
output: {
greeting: string;
};
transformer: false;
errorShape: DefaultErrorShape;
}>> (+1 overload)
useQuery({ name: stringname: 'Bob' });
const const goodbyeMutation: UseTRPCMutationResult<string, TRPCClientErrorLike<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>, void, unknown>
goodbyeMutation = const trpc: CreateTRPCReactBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: {
name: string;
};
output: {
greeting: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
goodbye: MutationProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, unknown> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>
trpc.goodbye: DecoratedMutation<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>
goodbye.useMutation: <unknown>(opts?: UseTRPCMutationOptions<void, TRPCClientErrorLike<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>, string, unknown> | undefined) => UseTRPCMutationResult<string, TRPCClientErrorLike<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>, void, unknown>
useMutation();
return (
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>{const helloQuery: UseTRPCQueryResult<{
greeting: string;
}, TRPCClientErrorLike<{
input: {
name: string;
};
output: {
greeting: string;
};
transformer: false;
errorShape: DefaultErrorShape;
}>>
helloQuery.data: {
greeting: string;
} | undefined
data?.greeting: string | undefinedgreeting}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
<React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() => const goodbyeMutation: UseTRPCMutationResult<string, TRPCClientErrorLike<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>, void, unknown>
goodbyeMutation.mutate: (variables?: void | undefined, options?: MutateOptions<string, TRPCClientErrorLike<{
input: void;
output: string;
transformer: false;
errorShape: DefaultErrorShape;
}>, void, unknown> | undefined) => void
mutate()}>Say Goodbye</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}Differences to vanilla React Query
The wrapper abstracts some aspects of React Query for you:
- Query Keys - these are generated and managed by tRPC on your behalf, based on the procedure inputs you provide
- If you need the query key which tRPC calculates, you can use getQueryKey
- Type safe by default - the types you provide in your tRPC Backend also drive the types of your React Query client, providing safety throughout your React app
Pages under React Query Integration (Classic, deprecated)
- SetupHow to use and setup tRPC in React
- Server Components
- Inferring Types
- useQuery()Fetch data with the classic trpc.procedure.useQuery() hook, its tRPC-specific options and streaming
- useMutation()Call mutations with the classic trpc.procedure.useMutation() hook
- useInfiniteQuery()
- useSubscription()
- useUtils()Invalidate, refetch, prefetch and update cached query data with the classic trpc.useUtils() helpers
- createTRPCQueryUtils()
- useQueries()
- Suspense
- getQueryKey()
- Aborting Procedure Calls
- Disabling Queries