LogoRobo.js

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.

src/trpc/client.ts
import { createTRPCClient, httpBatchLink } from '@robojs/trpc'
import type { AppRouter } from './server'

export const trpcClient = createTRPCClient<AppRouter>({
	links: [
		httpBatchLink({
			url: '/api/trpc'
		})
	]
})
src/trpc/client.js
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.

src/trpc/client.ts
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]
})
src/trpc/client.js
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:

src/app/App.tsx
import { TRPCProvider } from '@robojs/trpc'
import { trpc, trpcQueryClient } from '../trpc/client'

function App() {
	return (
		<TRPCProvider trpc={trpc} trpcClient={trpcQueryClient}>
			<Activity />
		</TRPCProvider>
	)
}
src/app/App.jsx
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

StandaloneReact Query
React requiredNoYes
CachingManualAutomatic
Loading/error statesManualBuilt-in
RefetchingManualAutomatic
Best forEvent handlers, scripts, non-React codeReact 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).

Links control how requests are sent to the server. The plugin re-exports several link types from tRPC.

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}` : ''
		}
	}
})

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' })
]

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' })
	})
]
LinkDescription
httpBatchStreamLinkLike httpBatchLink but with streaming support for large responses.
httpSubscriptionLinkServer-sent events for subscriptions over HTTP.
retryLinkAutomatically retry failed operations.
wsLinkWebSocket 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:

config/plugins/robojs/server.mjs
export default {
	prefix: '/v1'
}
config/plugins/robojs/server.mjs
export default {
	prefix: '/v1'
}
src/trpc/client.ts
const link = httpBatchLink({
	url: '/v1/trpc' // Must match server prefix
})
src/trpc/client.js
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:

src/app/App.tsx
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>
	)
}
src/app/App.jsx
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>
	)
}

Next Steps

On this page