Skip to content

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 ​

ts
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 ​

HelperRole
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 ​

ts
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 ​

ts
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 ​

ts
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

ts
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

ts
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

ts
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

ts
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

ts
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 }),
  })

MIT Licensed · Created by Mohammad Garmabi · Not an official TanStack package