/
githubmirror
/
trpc
Обзор
Документация
Войти
/
githubmirror
/
trpc
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
www/docs/client/tanstack-react-query/usage.mdx
1 017 строк
31 KB
Nick Lucas
feat(docs): revamp docs content for 2026, more framework agnostic, improved Next.js docs (#7193)
08 мар 2026, 12:14
Не верифицирован
08 мар 2026, 12:14
2bf122d
Код
Авторство
О чём код?
--- id: usage title: TanStack React Query sidebar_label: Usage description: TanStack React Query usage slug: /client/tanstack-react-query/usage --- ## Quick example query ```tsx twoslash // @filename: server/router.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ greeting: t.procedure.input(z.object({ name: z.string() })).query(({ input }) => `Hello ${input.name}` as const), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server/router'; export const { TRPCProvider, useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.tsx // ---cut--- import { useQuery } from '@tanstack/react-query'; import { useTRPC } from './trpc'; function Users() { const trpc = useTRPC(); const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' })); // greetingQuery.data === 'Hello Jerry' } ``` ## Usage The philosophy of this client is to provide thin and type-safe factories which work natively and type-safely with Tanstack React Query. This means just by following the autocompletes the client gives you, you can focus on building just with the knowledge the [TanStack React Query docs](https://tanstack.com/query/latest/docs/framework/react/overview) provide. ```tsx twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ id: z.string().optional() }).optional()).query(() => 'result'), mutation: t.procedure.mutation(() => 'ok'), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.tsx import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTRPC } from './trpc'; // ---cut--- export default function Basics() { const trpc = useTRPC(); const queryClient = useQueryClient(); // Create QueryOptions which can be passed to query hooks const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ }) const myQuery = useQuery(myQueryOptions) // or: // useSuspenseQuery(myQueryOptions) // useInfiniteQuery(myQueryOptions) // Create MutationOptions which can be passed to useMutation const myMutationOptions = trpc.path.to.mutation.mutationOptions() const myMutation = useMutation(myMutationOptions) // Create a QueryKey which can be used to manipulate many methods // on TanStack's QueryClient in a type-safe manner const myQueryKey = trpc.path.to.query.queryKey() const invalidateMyQueryKey = () => { queryClient.invalidateQueries({ queryKey: myQueryKey }) } return ( // Your app here null ) } ``` The `trpc` object is fully type-safe and will provide autocompletes for all the procedures in your `AppRouter`. At the end of the proxy, the following methods are available: ### `queryOptions` - querying data {#queryOptions} Available for all query procedures. Provides a type-safe wrapper around [Tanstack's `queryOptions` function](https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions). The first argument is the input for the procedure, and the second argument accepts any native Tanstack React Query options. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ id: z.string() })).query(() => 'result') }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryOptions = trpc.path.to.query.queryOptions( { /** input */ id: 'foo', }, { // Any Tanstack React Query options staleTime: 1000, }, ); // ---cut-after--- } ``` You can additionally provide a `trpc` object to the `queryOptions` function to provide tRPC request options to the client. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ id: z.string() })).query(() => 'result') }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryOptions = trpc.path.to.query.queryOptions( { /** input */ id: 'foo', }, { trpc: { // Provide tRPC request options to the client context: { // see https://trpc.io/docs/client/links#managing-context }, }, }, ); // ---cut-after--- } ``` If you want to disable a query in a type safe way, you can use `skipToken`: ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ user: t.router({ details: t.procedure.input(z.object({ userId: z.string(), projectId: z.string() })).query(() => ({ name: 'foo' })), }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useQuery, skipToken } from '@tanstack/react-query'; import { useTRPC } from './trpc'; declare const user: { id: string } | undefined; declare const project: { id: string } | undefined; function Component() { const trpc = useTRPC(); // ---cut--- const query = useQuery( trpc.user.details.queryOptions( user?.id && project?.id ? { userId: user.id, projectId: project.id, } : skipToken, { staleTime: 1000, }, ), ); // ---cut-after--- } ``` The result can be passed to `useQuery` or `useSuspenseQuery` hooks or query client methods like `fetchQuery`, `prefetchQuery`, `prefetchInfiniteQuery`, `invalidateQueries`, etc. ### `infiniteQueryOptions` - querying infinite data {#infiniteQueryOptions} Available for all query procedures that take a cursor input. Provides a type-safe wrapper around [Tanstack's `infiniteQueryOptions` function](https://tanstack.com/query/latest/docs/framework/react/reference/infiniteQueryOptions). The first argument is the input for the procedure, and the second argument accepts any native Tanstack React Query options. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ cursor: z.number().optional() })).query(() => ({ items: ['item'], nextCursor: 1 as number | undefined })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions( { /** input */ }, { // Any Tanstack React Query options getNextPageParam: (lastPage, pages) => lastPage.nextCursor, }, ); // ---cut-after--- } ``` ### `queryKey` - getting the query key and performing operations on the query client {#queryKey} Available for all query procedures. Allows you to access the query key in a type-safe manner. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ id: z.string() })).query(() => 'result') }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryKey = trpc.path.to.query.queryKey(); // ---cut-after--- } ``` Since Tanstack React Query uses fuzzy matching for query keys, you can also create a partial query key for any sub-path to match all queries belonging to a router: ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const appRouter = t.router({ router: t.router({ someQuery: t.procedure.query(() => 'result') }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryKey = trpc.router.pathKey(); // ---cut-after--- } ``` Or even the root path to match all tRPC queries: ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const appRouter = t.router({ someQuery: t.procedure.query(() => 'result') }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryKey = trpc.pathKey(); // ---cut-after--- } ``` ### `infiniteQueryKey` - getting the infinite query key {#infiniteQueryKey} Available for all query procedures that take a cursor input. Allows you to access the query key for an infinite query in a type-safe manner. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ cursor: z.number().optional() })).query(() => ({ items: ['item'], nextCursor: 1 as number | undefined })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({ /** input */ }); // ---cut-after--- } ``` The result can be used with query client methods like `getQueryData`, `setQueryData`, `invalidateQueries`, etc. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ cursor: z.number().optional() })).query(() => ({ items: ['item'], nextCursor: 1 as number | undefined })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useQueryClient } from '@tanstack/react-query'; import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); const queryClient = useQueryClient(); // ---cut--- // Get cached data for an infinite query const cachedData = queryClient.getQueryData( trpc.path.to.query.infiniteQueryKey({ cursor: 0 }), ); // Set cached data for an infinite query queryClient.setQueryData( trpc.path.to.query.infiniteQueryKey({ cursor: 0 }), (data) => { // Modify the data return data; }, ); // ---cut-after--- } ``` ### `queryFilter` - creating query filters {#queryFilter} Available for all query procedures. Allows creating [query filters](https://tanstack.com/query/latest/docs/framework/react/guides/filters#query-filters) in a type-safe manner. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ id: z.string() })).query(() => 'result') }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryFilter = trpc.path.to.query.queryFilter( { /** input */ }, { // Any Tanstack React Query filter predicate: (query) => { return !!query.state.data; }, }, ); // ---cut-after--- } ``` Like with query keys, if you want to run a filter across a whole router you can use `pathFilter` to target any sub-path. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ someQuery: t.procedure.query(() => 'result') }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const queryFilter = trpc.path.pathFilter({ // Any Tanstack React Query filter predicate: (query) => { return !!query.state.data; }, }); // ---cut-after--- } ``` Useful for creating filters that can be passed to client methods like `queryClient.invalidateQueries` etc. ### `infiniteQueryFilter` - creating infinite query filters {#infiniteQueryFilter} Available for all query procedures that take a cursor input. Allows creating [query filters](https://tanstack.com/query/latest/docs/framework/react/guides/filters#query-filters) for infinite queries in a type-safe manner. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ cursor: z.number().optional() })).query(() => ({ items: ['item'], nextCursor: 1 as number | undefined })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter( { /** input */ }, { // Any Tanstack React Query filter predicate: (query) => { return !!query.state.data; }, }, ); // ---cut-after--- } ``` Useful for creating filters that can be passed to client methods like `queryClient.invalidateQueries` etc. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ query: t.procedure.input(z.object({ cursor: z.number().optional() })).query(() => ({ items: ['item'], nextCursor: 1 as number | undefined })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useQueryClient } from '@tanstack/react-query'; import { useTRPC } from './trpc'; async function Component() { const trpc = useTRPC(); const queryClient = useQueryClient(); // ---cut--- await queryClient.invalidateQueries( trpc.path.to.query.infiniteQueryFilter( {}, { predicate: (query) => { // Filter logic based on query state return query.state.status === 'success'; }, }, ), ); // ---cut-after--- } ``` ### `mutationOptions` - creating mutation options {#mutationOptions} Available for all mutation procedures. Provides a type-safe identity function for constructing options that can be passed to `useMutation`. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ mutation: t.procedure.input(z.object({ id: z.string() })).mutation(() => 'ok' as const), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const mutationOptions = trpc.path.to.mutation.mutationOptions({ // Any Tanstack React Query options onSuccess: (data) => { // do something with the data }, }); // ---cut-after--- } ``` ### `mutationKey` - getting the mutation key {#mutationKey} Available for all mutation procedures. Allows you to get the mutation key in a type-safe manner. ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ mutation: t.procedure.input(z.object({ id: z.string() })).mutation(() => 'ok' as const), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; function Component() { const trpc = useTRPC(); // ---cut--- const mutationKey = trpc.path.to.mutation.mutationKey(); // ---cut-after--- } ``` ### `subscriptionOptions` - creating subscription options {#subscriptionOptions} TanStack does not provide a subscription hook, so we continue to expose our own abstraction here which works with a [standard tRPC subscription setup](/docs/server/subscriptions). Available for all subscription procedures. Provides a type-safe identity function for constructing options that can be passed to `useSubscription`. Note that you need to have either the [`httpSubscriptionLink`](/docs/client/links/httpSubscriptionLink) or [`wsLink`](/docs/client/links/wsLink) configured in your tRPC client to use subscriptions. ```tsx twoslash // @jsx: react-jsx // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ subscription: t.procedure.input(z.object({ channel: z.string().optional() }).optional()).subscription(async function* () { yield 'data' as string; }), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.tsx import { useTRPC } from './trpc'; import { useSubscription } from '@trpc/tanstack-react-query'; // ---cut--- function SubscriptionExample() { const trpc = useTRPC(); const subscription = useSubscription( trpc.path.to.subscription.subscriptionOptions( { /** input */ }, { enabled: true, onStarted: () => { // do something when the subscription is started }, onData: (data) => { // you can handle the data here }, onError: (error) => { // you can handle the error here }, onConnectionStateChange: (state) => { // you can handle the connection state here }, }, ), ); // Or you can handle the state here subscription.data; // The lastly received data subscription.error; // The lastly received error /** * The current status of the subscription. * Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`. * * - `idle`: subscription is disabled or ended * - `connecting`: trying to establish a connection * - `pending`: connected to the server, receiving data * - `error`: an error occurred and the subscription is stopped */ subscription.status; // Reset the subscription (if you have an error etc) subscription.reset(); return <>{/* ... */}</>; } ``` ### Query Key Prefixing {#keyPrefix} When using multiple tRPC providers in a single application (e.g., connecting to different backend services), queries with the same path will collide in the cache. You can prevent this by enabling query key prefixing. ```tsx twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const authRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); const billingRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); export type AuthRouter = typeof authRouter; export type BillingRouter = typeof billingRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AuthRouter, BillingRouter } from './server'; const auth = createTRPCContext<AuthRouter>(); const billing = createTRPCContext<BillingRouter>(); export const useTRPCAuth = auth.useTRPC; export const useTRPCBilling = billing.useTRPC; // @filename: component.ts import { useQuery } from '@tanstack/react-query'; import { useTRPCAuth, useTRPCBilling } from './trpc'; function Component() { const trpcAuth = useTRPCAuth(); const trpcBilling = useTRPCBilling(); // ---cut--- // Without prefixes - these would collide! const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service // ---cut-after--- } ``` Enable the feature flag when creating your context: ```tsx twoslash title='utils/trpc.ts' // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const billingRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); const accountRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); export type BillingRouter = typeof billingRouter; export type AccountRouter = typeof accountRouter; // @filename: utils/trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import { createTRPCClient } from '@trpc/client'; import type { BillingRouter, AccountRouter } from '../server'; // ---cut--- // [...] const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>(); export const BillingProvider = billing.TRPCProvider; export const useBilling = billing.useTRPC; export const createBillingClient = () => createTRPCClient<BillingRouter>({ links: [ /* ... */ ], }); const account = createTRPCContext<AccountRouter, { keyPrefix: true }>(); export const AccountProvider = account.TRPCProvider; export const useAccount = account.useTRPC; export const createAccountClient = () => createTRPCClient<AccountRouter>({ links: [ /* ... */ ], }); ``` ```tsx twoslash title='App.tsx' // @jsx: react-jsx // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const billingRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); const accountRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); export type BillingRouter = typeof billingRouter; export type AccountRouter = typeof accountRouter; // @filename: utils/trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import { createTRPCClient } from '@trpc/client'; import type { BillingRouter, AccountRouter } from '../server'; const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>(); export const BillingProvider = billing.TRPCProvider; export const createBillingClient = () => createTRPCClient<BillingRouter>({ links: [] }); const account = createTRPCContext<AccountRouter, { keyPrefix: true }>(); export const AccountProvider = account.TRPCProvider; export const createAccountClient = () => createTRPCClient<AccountRouter>({ links: [] }); // @filename: App.tsx // ---cut--- import { useState } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { BillingProvider, AccountProvider, createBillingClient, createAccountClient, } from './utils/trpc'; // [...] export function App() { const [queryClient] = useState(() => new QueryClient()); const [billingClient] = useState(() => createBillingClient()); const [accountClient] = useState(() => createAccountClient()); return ( <QueryClientProvider client={queryClient}> <BillingProvider trpcClient={billingClient} queryClient={queryClient} keyPrefix="billing" > <AccountProvider trpcClient={accountClient} queryClient={queryClient} keyPrefix="account" > <div>{/* ... */}</div> </AccountProvider> </BillingProvider> </QueryClientProvider> ); } ``` ```tsx twoslash title='components/MyComponent.tsx' // @jsx: react-jsx // @filename: server.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); const billingRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); const accountRouter = t.router({ list: t.procedure.query(() => [{ id: '1' }]) }); export type BillingRouter = typeof billingRouter; export type AccountRouter = typeof accountRouter; // @filename: utils/trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { BillingRouter, AccountRouter } from '../server'; const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>(); export const useBilling = billing.useTRPC; const account = createTRPCContext<AccountRouter, { keyPrefix: true }>(); export const useAccount = account.useTRPC; // @filename: components/MyComponent.tsx // ---cut--- import { useQuery } from '@tanstack/react-query'; import { useBilling, useAccount } from '../utils/trpc'; // [...] export function MyComponent() { const billing = useBilling(); const account = useAccount(); const billingList = useQuery(billing.list.queryOptions()); const accountList = useQuery(account.list.queryOptions()); return ( <div> <div>Billing: {JSON.stringify(billingList.data ?? null)}</div> <div>Account: {JSON.stringify(accountList.data ?? null)}</div> </div> ); } ``` The query keys will be properly prefixed to avoid collisions: ```tsx twoslash // Example of how the query keys look with prefixes const queryKeys = [ [['billing'], ['list'], { type: 'query' }], [['account'], ['list'], { type: 'query' }], ]; ``` ### Inferring Input and Output types When you need to infer the input and output types for a procedure or router, there are 2 options available depending on the situation. Infer the input and output types of a full router ```ts twoslash // @filename: server/router.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ getUser: t.procedure.input(z.object({ id: z.string() })).query(() => ({ name: 'foo' })), }); export type AppRouter = typeof appRouter; // @filename: types.ts // ---cut--- import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'; import type { AppRouter } from './server/router'; export type Inputs = inferRouterInputs<AppRouter>; export type Outputs = inferRouterOutputs<AppRouter>; ``` Infer types for a single procedure ```ts twoslash // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ procedure: t.procedure.input(z.object({ id: z.string() })).query(() => ({ name: 'foo' })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server'; export const { useTRPC } = createTRPCContext<AppRouter>(); // @filename: component.ts import { useTRPC } from './trpc'; // ---cut--- import type { inferInput, inferOutput } from '@trpc/tanstack-react-query'; function Component() { const trpc = useTRPC(); type Input = inferInput<typeof trpc.path.to.procedure>; type Output = inferOutput<typeof trpc.path.to.procedure>; } ``` ### Accessing the tRPC client {#useTRPCClient} If you used the [setup with React Context](/docs/client/tanstack-react-query/setup#3a-set-up-the-trpc-context-provider), you can access the tRPC client using the `useTRPCClient` hook. ```tsx twoslash // @filename: server/router.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ getUser: t.procedure.input(z.object({ id: z.string() })).query(() => ({ name: 'foo' })), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCContext } from '@trpc/tanstack-react-query'; import type { AppRouter } from './server/router'; export const { TRPCProvider, useTRPC, useTRPCClient } = createTRPCContext<AppRouter>(); // @filename: component.tsx // ---cut--- import { useTRPCClient } from './trpc'; async function Component() { const trpcClient = useTRPCClient(); const result = await trpcClient.getUser.query({ id: '1', }); } ``` If you [setup without React Context](/docs/client/tanstack-react-query/setup#3c-set-up-without-react-context), you can import the global client instance directly instead. ```ts twoslash // @module: esnext // @target: esnext // @filename: server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ path: t.router({ to: t.router({ procedure: t.procedure.input(z.object({ id: z.string() })).query(() => ({ name: 'foo' })), }) }), }); export type AppRouter = typeof appRouter; // @filename: trpc.ts import { createTRPCClient } from '@trpc/client'; import type { AppRouter } from './server'; export const client = createTRPCClient<AppRouter>({ links: [] }); // @filename: example.ts // ---cut--- import { client } from './trpc'; const result = await client.path.to.procedure.query({ /** input */ id: 'foo', }); ```