tRPC
createTRPCFetchClient / createTRPCFetch is framework-agnostic. Prefer TanStack Start + TanStack Query with router context when you can.
Use the Framework picker in the header to show one stack at a time.
Full recipe (Start, Router context, SolidStart, Remix): tRPC + tanstack-fetch.
Example app: examples/trpc.
Shared client
import { createFetch } from 'tanstack-fetch'
import {
createAuthInterceptor,
createRetryIdempotentInterceptor,
createStatusInterceptor,
createTraceInterceptor,
} from 'tanstack-fetch/plugins'
import { createTRPCFetchClient } from 'tanstack-fetch/trpc'
import type { AppRouter } from './server'
export const api = createFetch({
interceptors: [
createAuthInterceptor({ getToken: () => localStorage.getItem('access_token') }),
createStatusInterceptor({ 401: () => localStorage.removeItem('access_token') }),
createTraceInterceptor(),
createRetryIdempotentInterceptor(),
],
})
export const trpcClient = createTRPCFetchClient<AppRouter>({
url: '/api/trpc',
client: api,
})Helpers
| Helper | Role |
|---|---|
createTRPCFetch(api) | fetch for httpBatchLink |
createTRPCFetchLink({ url, client }) | ready terminating link |
createTRPCFetchClient<AppRouter>(…) | full client |
TanStack Start + TanStack Query
Framework
TanStack Start · @tanstack/react-query · Router context
Client + proxy
import { createFetch } from 'tanstack-fetch'
import { createSsrForwardInterceptor, createTraceInterceptor } from 'tanstack-fetch/plugins'
import { createTRPCFetchClient } from 'tanstack-fetch/trpc'
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query'
import { QueryClient } from '@tanstack/react-query'
import type { AppRouter } from '~/server/router'
const getTrpcUrl = () =>
typeof window === 'undefined'
? `${process.env.SSR_ORIGIN ?? 'http://localhost:3000'}/api/trpc`
: '/api/trpc'
export const api = createFetch({
source: typeof window === 'undefined' ? 'ssr' : 'browser',
interceptors: [createSsrForwardInterceptor(), createTraceInterceptor()],
})
export const queryClient = new QueryClient()
export const trpc = createTRPCOptionsProxy<AppRouter>({
client: createTRPCFetchClient<AppRouter>({ url: getTrpcUrl(), client: api }),
queryClient,
})Router context
import { createRouter } from '@tanstack/react-router'
import { QueryClientProvider } from '@tanstack/react-query'
import { queryClient, trpc } from '~/lib/trpc'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
context: { trpc, queryClient },
Wrap: ({ children }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
),
})Loader + component
import { createFileRoute } from '@tanstack/react-router'
import { useQuery } from '@tanstack/react-query'
export const Route = createFileRoute('/posts')({
loader: ({ context }) =>
context.queryClient.ensureQueryData(context.trpc.post.list.queryOptions()),
component: () => {
const { trpc } = Route.useRouteContext()
const { data } = useQuery(trpc.post.list.queryOptions())
return <pre>{JSON.stringify(data, null, 2)}</pre>
},
})More patterns (nested params, beforeLoad): recipe.
React
Framework
React · @trpc/tanstack-react-query · @tanstack/react-query
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query'
import { QueryClient, useQuery } from '@tanstack/react-query'
import { trpcClient } from './trpc'
const queryClient = new QueryClient()
const trpc = createTRPCOptionsProxy<AppRouter>({ client: trpcClient, queryClient })
useQuery(trpc.post.list.queryOptions())Router context (SPA): same as Start — see recipe · Router.
Remix
Framework
Remix · loader + optional TanStack Query hydration
Create a per-request server client with ssr-forward + request cookies, then loader → json. Full example: recipe · Remix.
Next.js
Framework
React · Next.js App Router
Same React setup. Relative /api/trpc in the browser, absolute origin on the server. Forward cookies with ssr-forward when needed.
SolidStart
Framework
SolidStart · @tanstack/solid-query · @trpc/client
Absolute URL on the server, relative in the browser — same SSR pattern as Start. See recipe · SolidStart.
Vue / Nuxt
Framework
Vue 3 / Nuxt · @tanstack/vue-query · @trpc/client
import { createTRPCClient, httpBatchLink } from '@trpc/client'
import { createTRPCFetch } from 'tanstack-fetch/trpc'
import { useQuery } from '@tanstack/vue-query'
import { api } from './api'
import type { AppRouter } from './server'
export const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
fetch: createTRPCFetch(api),
}),
],
})
const posts = useQuery({
queryKey: ['posts'],
queryFn: ({ signal }) => trpc.post.list.query(undefined, { signal }),
})Solid
Framework
Solid · @tanstack/solid-query · @trpc/client · optional tanstack-fetch/solid
import { createTRPCClient, httpBatchLink } from '@trpc/client'
import { createTRPCFetch } from 'tanstack-fetch/trpc'
import { createQuery } from '@tanstack/solid-query'
import { api } from './api'
import type { AppRouter } from './server'
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: '/api/trpc', fetch: createTRPCFetch(api) })],
})
const posts = createQuery(() => ({
queryKey: ['posts'],
queryFn: ({ signal }) => trpc.post.list.query(undefined, { signal }),
}))Angular
Framework
Angular · @tanstack/angular-query-experimental · @trpc/client · optional tanstack-fetch/angular
import { createTRPCClient, httpBatchLink } from '@trpc/client'
import { createTRPCFetch } from 'tanstack-fetch/trpc'
import { injectQuery } from '@tanstack/angular-query-experimental'
import { api } from './api'
import type { AppRouter } from './server'
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: '/api/trpc', fetch: createTRPCFetch(api) })],
})
readonly posts = injectQuery(() => ({
queryKey: ['posts'],
queryFn: ({ signal }: { signal: AbortSignal }) =>
trpc.post.list.query(undefined, { signal }),
}))Svelte / SvelteKit
Framework
Svelte / SvelteKit · @tanstack/svelte-query · @trpc/client · optional tanstack-fetch/svelte
import { createTRPCClient, httpBatchLink } from '@trpc/client'
import { createTRPCFetch } from 'tanstack-fetch/trpc'
import { createQuery } from '@tanstack/svelte-query'
import { api } from './api'
import type { AppRouter } from './server'
export const trpc = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: '/api/trpc', fetch: createTRPCFetch(api) })],
})
export const postsQuery = () =>
createQuery({
queryKey: ['posts'],
queryFn: ({ signal }) => trpc.post.list.query(undefined, { signal }),
})Related
- Recipe · tRPC (Start, Router context, SolidStart, Remix)
- TanStack Query
- SSR
- Migrate scaffolds
