Skip to content
tRPC

TanStack React Query

TanStack React Query setup

On this page

Compared to our classic React Query Integration this client is simpler and more TanStack Query-native, providing factories for common TanStack React Query interfaces like QueryKeys, QueryOptions, and MutationOptions. We think it's the future and recommend using this over the classic client, read the announcement post for more information about this change.

❓ 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.

utils/trpc.ts
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: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>> & DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        list: QueryProcedure<{
            input: void;
            output: {
                id: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>
AppRouter
} from '../server/router';
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: string;
                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: string;
                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: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>> & DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        list: QueryProcedure<{
            input: void;
            output: {
                id: string;
                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: string;
                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: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>>>(opts: HTTPBatchLinkOptions<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>): TRPCLink<...>
httpBatchLink
({ url: string | URLurl: 'YOUR_API_URL' })],
});
components/PostList.tsx
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: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>>>
trpc
} from './utils/trpc';
function function PostList(): voidPostList() { const {
const data: {
    id: string;
    title: string;
}[] | undefined
data
} =
useQuery<{
    id: string;
    title: string;
}[], Error, {
    id: string;
    title: string;
}[], readonly ["posts"]>(options: UndefinedInitialDataOptions<{
    id: string;
    title: string;
}[], Error, {
    id: string;
    title: string;
}[], readonly ["posts"]>, queryClient?: QueryClient): UseQueryResult<{
    id: string;
    title: string;
}[], Error> (+2 overloads)
useQuery
({
queryKey: readonly ["posts"]queryKey: ['posts'] as type const = readonly ["posts"]const,
queryFn?: unique symbol | QueryFunction<{
    id: string;
    title: string;
}[], readonly ["posts"], 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: string;
                title: string;
            }[];
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>>>
trpc
.
post: DecoratedProcedureRecord<{
    transformer: false;
    errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
    list: QueryProcedure<{
        input: void;
        output: {
            id: string;
            title: string;
        }[];
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>
post
.
list: {
    query: Resolver<{
        input: void;
        output: {
            id: string;
            title: string;
        }[];
        errorShape: DefaultErrorShape;
        transformer: false;
    }>;
}
list
.
query: (input: void, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<{
    id: string;
    title: string;
}[], DefaultErrorShape>
query
(),
});
const data: {
    id: string;
    title: string;
}[] | undefined
data
; // Post[]
// ... }

Setup

1. Install dependencies

The following dependencies should be installed

pnpm add @trpc/server @trpc/client @trpc/tanstack-react-query @tanstack/react-query
npm install @trpc/server @trpc/client @trpc/tanstack-react-query @tanstack/react-query
yarn add @trpc/server @trpc/client @trpc/tanstack-react-query @tanstack/react-query
bun add @trpc/server @trpc/client @trpc/tanstack-react-query @tanstack/react-query

2. Import your AppRouter

Import your AppRouter type into the client application. This type holds the shape of your entire API.

utils/trpc.ts
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<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: {
                id: string;
                title: string;
            };
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>> & DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: {
                id: string;
                title: string;
            };
            meta: object;
            errorShape: DefaultErrorShape;
        }>;
    }>>;
}>
AppRouter
} from '../server/router';

3a. Set up the tRPC context provider

In cases where you rely on React context, such as when using server-side rendering in full-stack frameworks like Next.js, it's important to create a new QueryClient for each request so that your users don't end up sharing the same cache, you can use the createTRPCContext to create a set of type-safe context providers and consumers from your AppRouter type signature.

utils/trpc.ts
import { 
function createTRPCContext<TRouter extends AnyRouter, TFeatureFlags extends FeatureFlags = {
    keyPrefix: false;
}>(): CreateTRPCContextResult<TRouter, TFeatureFlags>
createTRPCContext
} from '@trpc/tanstack-react-query';
import type {
type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
} from '../server/router';
export const {
const TRPCProvider: TRPCProviderType<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
TRPCProvider
,
const useTRPC: () => TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
useTRPC
,
const useTRPCClient: () => TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
useTRPCClient
} =
createTRPCContext<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>(): CreateTRPCContextResult<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>, {
    keyPrefix: false;
}>
createTRPCContext
<
type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
>();

Then, create a tRPC client, and wrap your application in the TRPCProvider, as below. You will also need to set up and connect React Query, which they document in more depth.

components/App.tsx
import { class QueryClientQueryClient, const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider } from '@tanstack/react-query';
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 { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)useState } from 'react';
import type { 
type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
} from '../server/router';
import {
const TRPCProvider: TRPCProviderType<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
TRPCProvider
} from '../utils/trpc';
function function makeQueryClient(): QueryClientmakeQueryClient() { return new new QueryClient(config?: QueryClientConfig): QueryClientQueryClient({ QueryClientConfig.defaultOptions?: DefaultOptions<Error> | undefineddefaultOptions: { DefaultOptions<Error>.queries?: OmitKeyof<QueryObserverOptions<unknown, Error, unknown, unknown, readonly unknown[], never>, "suspense" | "queryKey", "strictly"> | undefinedqueries: { // With SSR, we usually want to set some default staleTime // above 0 to avoid refetching immediately on the client staleTime?: StaleTimeFunction<unknown, Error, unknown, readonly unknown[]> | undefinedstaleTime: 60 * 1000, }, }, }); } let let browserQueryClient: QueryClient | undefinedbrowserQueryClient: class QueryClientQueryClient | undefined = var undefinedundefined; function function getQueryClient(): QueryClientgetQueryClient() { if (typeof var window: Window & typeof globalThiswindow === 'undefined') { // Server: always make a new query client return function makeQueryClient(): QueryClientmakeQueryClient(); } else { // Browser: make a new query client if we don't already have one // This is very important, so we don't re-make a new client if React // suspends during the initial render. This may not be needed if we // have a suspense boundary BELOW the creation of the query client if (!let browserQueryClient: QueryClient | undefinedbrowserQueryClient) let browserQueryClient: QueryClient | undefinedbrowserQueryClient = function makeQueryClient(): QueryClientmakeQueryClient(); return let browserQueryClient: QueryClientbrowserQueryClient; } } export function function App(): React.JSX.ElementApp() { const const queryClient: QueryClientqueryClient = function getQueryClient(): QueryClientgetQueryClient(); const [
const trpcClient: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
trpcClient
] =
useState<TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>>(initialState: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>> | (() => TRPCClient<...>)): [...] (+1 overload)
useState
(() =>
createTRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: 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<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
>({
links: TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>[]
links
: [
httpBatchLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>(opts: HTTPBatchLinkOptions<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>): TRPCLink<...>
httpBatchLink
({
url: string | URLurl: 'http://localhost:2022', }), ], }), ); return ( <const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider client: QueryClientclient={const queryClient: QueryClientqueryClient}> <
const TRPCProvider: TRPCProviderType<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
TRPCProvider
trpcClient: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
trpcClient
={
const trpcClient: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
trpcClient
} queryClient: QueryClientqueryClient={const queryClient: QueryClientqueryClient}>
{null /* Your app here */} </
const TRPCProvider: TRPCProviderType<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
TRPCProvider
>
</const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider> ); }

3b. Set up with Query/Mutation Key Prefixing enabled

If you want to prefix all queries and mutations with a specific key, see Query Key Prefixing for setup and usage examples.

3c. Set up without React context

When building an SPA using only client-side rendering with something like Vite, you can create the QueryClient and tRPC client outside of React context as singletons.

utils/trpc.ts
import { class QueryClientQueryClient } from '@tanstack/react-query';
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 { 
function createTRPCOptionsProxy<TRouter extends AnyRouter, TFeatureFlags extends FeatureFlags = {
    keyPrefix: false;
}>(opts: TRPCOptionsProxyOptions<TRouter, TFeatureFlags>): TRPCOptionsProxy<TRouter, TFeatureFlags>
createTRPCOptionsProxy
} from '@trpc/tanstack-react-query';
import type {
type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
} from '../server/router';
export const const queryClient: QueryClientqueryClient = new new QueryClient(config?: QueryClientConfig): QueryClientQueryClient(); const
const trpcClient: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
trpcClient
=
createTRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: 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<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
>({
links: TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>[]
links
: [
httpBatchLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>(opts: HTTPBatchLinkOptions<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>): TRPCLink<...>
httpBatchLink
({ url: string | URLurl: 'http://localhost:2022' })],
}); export const
const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
trpc
=
createTRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>(opts: TRPCOptionsProxyOptions<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>, {
    keyPrefix: false;
}>): TRPCOptionsProxy<...>
createTRPCOptionsProxy
<
type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>> & DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>
AppRouter
>({
TRPCOptionsProxyOptionsExternal<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ getUser: QueryProcedure<{ input: { id: string; }; output: { name: string; }; meta: object; errorShape: DefaultErrorShape; }>; createUser: MutationProcedure<...>; }>>>.client: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>> | TRPCUntypedClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>>
client
:
const trpcClient: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>>
trpcClient
,
queryClient: QueryClient | (() => QueryClient)queryClient, });
components/App.tsx
import { const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider } from '@tanstack/react-query';
import { const queryClient: QueryClientqueryClient } from '../utils/trpc';

export function function App(): React.JSX.ElementApp() {
  return (
    <const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider client: QueryClientclient={const queryClient: QueryClientqueryClient}>
      {/* Your app here */}
    </const QueryClientProvider: ({ client, children }: QueryClientProviderProps) => React.JSX.ElementQueryClientProvider>
  );
}

4. Fetch data

You can now use the tRPC React Query integration to call queries and mutations on your API.

components/user-list.tsx
import { function useMutation<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(options: UseMutationOptions<TData, TError, TVariables, TOnMutateResult>, queryClient?: QueryClient): UseMutationResult<TData, TError, TVariables, TOnMutateResult>useMutation, 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 useTRPC: () => TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
useTRPC
} from '../utils/trpc';
export default function function UserList(): React.JSX.ElementUserList() { const
const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
trpc
=
function useTRPC(): TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
useTRPC
(); // use `import { trpc } from './utils/trpc'` if you're using the singleton pattern
const
const userQuery: UseQueryResult<() => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>>
userQuery
=
useQuery<() => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, () => never, TRPCQueryKeyWithoutPrefix>(options: UndefinedInitialDataOptions<() => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, () => never, TRPCQueryKeyWithoutPrefix>, queryClient?: QueryClient): UseQueryResult<() => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>> (+2 overloads)
useQuery
(
const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
trpc
.
getUser: DecorateQueryProcedure<{
    input: {
        id: string;
    };
    output: {
        name: string;
    };
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}> & Record<string, never>
getUser
.
DecorateQueryProcedure<{ input: { id: string; }; output: { name: string; }; transformer: false; errorShape: DefaultErrorShape; featureFlags: { keyPrefix: false; }; }>.queryOptions: TRPCQueryOptions
<() => never, () => never>(input: {
    id: string;
}, opts?: UnusedSkipTokenTRPCQueryOptionsIn<() => never, () => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> | undefined) => UnusedSkipTokenTRPCQueryOptionsOut<() => never, () => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> (+2 overloads)
queryOptions
({ id: stringid: 'id_bilbo' }));
const
const userCreator: UseMutationResult<string, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, {
    name: string;
}, undefined>
userCreator
=
useMutation<string, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, {
    name: string;
}, undefined>(options: UseMutationOptions<string, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, {
    name: string;
}, undefined>, queryClient?: QueryClient): UseMutationResult<...>
useMutation
(
const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    getUser: QueryProcedure<{
        input: {
            id: string;
        };
        output: {
            name: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    createUser: MutationProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>, {
    keyPrefix: false;
}>
trpc
.
createUser: DecorateMutationProcedure<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>
createUser
.
DecorateMutationProcedure<{ input: { name: string; }; output: string; transformer: false; errorShape: DefaultErrorShape; featureFlags: { keyPrefix: false; }; }>.mutationOptions: TRPCMutationOptions
<undefined>(opts?: TRPCMutationOptionsIn<{
    name: string;
}, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, string, undefined, {
    keyPrefix: false;
}> | undefined) => TRPCMutationOptionsOut<{
    name: string;
}, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, string, undefined, {
    keyPrefix: false;
}>
mutationOptions
());
return ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>{
const userQuery: UseQueryResult<() => never, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>>
userQuery
.data: (() => never) | undefineddata?.Function.name: string | undefinedname}</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 userCreator: UseMutationResult<string, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, {
    name: string;
}, undefined>
userCreator
.
mutate: (variables: {
    name: string;
}, options?: MutateOptions<string, TRPCClientErrorLike<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}>, {
    name: string;
}, undefined> | undefined) => void
mutate
({ name: stringname: 'Frodo' })}>
Create Frodo </React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> ); }