Client
Call tRPC procedures with React Query hooks or a standalone client.
The plugin provides two ways to call tRPC procedures from the client: React Query hooks for React apps with automatic caching, and a standalone client for direct calls without React.
Both are set up in the seeded src/trpc/client.ts file and share the same type safety through the AppRouter type.
Standalone client
The standalone client calls procedures directly and returns promises. No React required.
import { createTRPCClient, httpBatchLink } from '@robojs/trpc'
import type { AppRouter } from './server'
export const trpcClient = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc'
})
]
})import { createTRPCClient, httpBatchLink } from '@robojs/trpc'
export const trpcClient = createTRPCClient({
links: [
httpBatchLink({
url: '/api/trpc'
})
]
})Use it anywhere — event handlers, server-side scripts, vanilla JavaScript:
// Query
const greeting = await trpcClient.hello.query({ text: 'World' })
// Mutation
const result = await trpcClient.createUser.mutate({
name: 'Alice',
email: 'alice@example.com'
})// Query
const greeting = await trpcClient.hello.query({ text: 'World' })
// Mutation
const result = await trpcClient.createUser.mutate({
name: 'Alice',
email: 'alice@example.com'
})React Query hooks
For React apps, createTRPCReact generates typed hooks that integrate with React Query. You get automatic caching, loading states, error handling, and refetching.
import { createTRPCReact, httpBatchLink } from '@robojs/trpc'
import type { AppRouter } from './server'
const batchLink = httpBatchLink({
url: '/api/trpc'
})
export const trpc = createTRPCReact<AppRouter>()
export const trpcQueryClient = trpc.createClient({
links: [batchLink]
})import { createTRPCReact, httpBatchLink } from '@robojs/trpc'
const batchLink = httpBatchLink({
url: '/api/trpc'
})
export const trpc = createTRPCReact()
export const trpcQueryClient = trpc.createClient({
links: [batchLink]
})Setting up TRPCProvider
Wrap your app in TRPCProvider to enable the hooks:
import { TRPCProvider } from '@robojs/trpc'
import { trpc, trpcQueryClient } from '../trpc/client'
function App() {
return (
<TRPCProvider trpc={trpc} trpcClient={trpcQueryClient}>
<Activity />
</TRPCProvider>
)
}import { TRPCProvider } from '@robojs/trpc'
import { trpc, trpcQueryClient } from '../trpc/client'
function App() {
return (
<TRPCProvider trpc={trpc} trpcClient={trpcQueryClient}>
<Activity />
</TRPCProvider>
)
}TRPCProvider wraps both tRPC's context and React Query's QueryClientProvider, so you don't need to set those up separately.
Props
Prop
Type
Using hooks
import { trpc } from '../trpc/client'
function UserList() {
const { data, isLoading, error } = trpc.user.list.useQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}import { trpc } from '../trpc/client'
function UserList() {
const { data, isLoading, error } = trpc.user.list.useQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}Mutations with hooks
import { trpc } from '../trpc/client'
function CreateUser() {
const mutation = trpc.createUser.useMutation()
const handleSubmit = (name: string, email: string) => {
mutation.mutate({ name, email })
}
return (
<div>
<button onClick={() => handleSubmit('Alice', 'alice@example.com')}>
Create User
</button>
{mutation.isPending && <span>Creating...</span>}
{mutation.error && <span>Error: {mutation.error.message}</span>}
{mutation.data && <span>Created: {mutation.data.name}</span>}
</div>
)
}import { trpc } from '../trpc/client'
function CreateUser() {
const mutation = trpc.createUser.useMutation()
const handleSubmit = (name, email) => {
mutation.mutate({ name, email })
}
return (
<div>
<button onClick={() => handleSubmit('Alice', 'alice@example.com')}>
Create User
</button>
{mutation.isPending && <span>Creating...</span>}
{mutation.error && <span>Error: {mutation.error.message}</span>}
{mutation.data && <span>Created: {mutation.data.name}</span>}
</div>
)
}Choosing a client mode
| Standalone | React Query | |
|---|---|---|
| React required | No | Yes |
| Caching | Manual | Automatic |
| Loading/error states | Manual | Built-in |
| Refetching | Manual | Automatic |
| Best for | Event handlers, scripts, non-React code | React components, UI data fetching |
You can use both in the same project. The seeded client.ts exports both trpcClient (standalone) and trpc (React Query hooks).
Link configuration
Links control how requests are sent to the server. The plugin re-exports several link types from tRPC.
httpBatchLink (default)
Batches multiple concurrent procedure calls into a single HTTP request. This is the default and recommended link.
import { httpBatchLink } from '@robojs/trpc'
const link = httpBatchLink({
url: '/api/trpc'
})import { httpBatchLink } from '@robojs/trpc'
const link = httpBatchLink({
url: '/api/trpc'
})Custom headers
Send authentication tokens or other headers with every request:
const link = httpBatchLink({
url: '/api/trpc',
async headers() {
const token = localStorage.getItem('auth_token')
return {
Authorization: token ? `Bearer ${token}` : ''
}
}
})const link = httpBatchLink({
url: '/api/trpc',
async headers() {
const token = localStorage.getItem('auth_token')
return {
Authorization: token ? `Bearer ${token}` : ''
}
}
})loggerLink
Log tRPC operations during development:
import { loggerLink, httpBatchLink } from '@robojs/trpc'
const links = [
loggerLink({
enabled: () => process.env.NODE_ENV === 'development'
}),
httpBatchLink({ url: '/api/trpc' })
]import { loggerLink, httpBatchLink } from '@robojs/trpc'
const links = [
loggerLink({
enabled: () => process.env.NODE_ENV === 'development'
}),
httpBatchLink({ url: '/api/trpc' })
]splitLink
Route different operation types to different links:
import { splitLink, httpBatchLink, wsLink, createWSClient } from '@robojs/trpc'
const wsClient = createWSClient({
url: 'ws://localhost:3000/api/trpc'
})
const links = [
splitLink({
condition: (op) => op.type === 'subscription',
true: wsLink({ client: wsClient }),
false: httpBatchLink({ url: '/api/trpc' })
})
]import { splitLink, httpBatchLink, wsLink, createWSClient } from '@robojs/trpc'
const wsClient = createWSClient({
url: 'ws://localhost:3000/api/trpc'
})
const links = [
splitLink({
condition: (op) => op.type === 'subscription',
true: wsLink({ client: wsClient }),
false: httpBatchLink({ url: '/api/trpc' })
})
]Other available links
| Link | Description |
|---|---|
httpBatchStreamLink | Like httpBatchLink but with streaming support for large responses. |
httpSubscriptionLink | Server-sent events for subscriptions over HTTP. |
retryLink | Automatically retry failed operations. |
wsLink | WebSocket transport for real-time subscriptions. |
Server prefix
The tRPC endpoint URL defaults to /api/trpc. If you change @robojs/server's prefix, update the client URL to match:
export default {
prefix: '/v1'
}export default {
prefix: '/v1'
}const link = httpBatchLink({
url: '/v1/trpc' // Must match server prefix
})const link = httpBatchLink({
url: '/v1/trpc' // Must match server prefix
})A mismatch between the server prefix and client URL causes 404 errors.
Custom QueryClient
Override React Query's defaults by passing a custom QueryClient:
import { QueryClient } from '@tanstack/react-query'
import { TRPCProvider } from '@robojs/trpc'
import { trpc, trpcQueryClient } from '../trpc/client'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
retry: 2,
refetchOnWindowFocus: false
}
}
})
function App() {
return (
<TRPCProvider trpc={trpc} trpcClient={trpcQueryClient} queryClient={queryClient}>
<Activity />
</TRPCProvider>
)
}import { QueryClient } from '@tanstack/react-query'
import { TRPCProvider } from '@robojs/trpc'
import { trpc, trpcQueryClient } from '../trpc/client'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
retry: 2,
refetchOnWindowFocus: false
}
}
})
function App() {
return (
<TRPCProvider trpc={trpc} trpcClient={trpcQueryClient} queryClient={queryClient}>
<Activity />
</TRPCProvider>
)
}