Skip to main content
Version: 10.x

Usage with React

info
  • If you're using Next.js, read the Usage with Next.js guide instead.
  • In order to infer types from your Node.js backend, you should have the frontend and backend in the same monorepo.

Add tRPC to an existing React project

Server Side

1. Install dependencies

bash
npm install @trpc/server zod
bash
npm install @trpc/server zod
Why Zod?

Zod is an input validator to ensure that our backend only processes requests that fit our API. You can use other validation libraries like Yup, Superstruct, io-ts if you prefer. In fact, any object containing a parse, create, or validateSync method will work.

2. Enable strict mode

If you want to use Zod for input validation, make sure you have enabled strict mode in your tsconfig.json:

tsconfig.json
diff
"compilerOptions": {
+ "strict": true
}
tsconfig.json
diff
"compilerOptions": {
+ "strict": true
}

If strict mode is too harsh, at least enable strictNullChecks:

tsconfig.json
diff
"compilerOptions": {
+ "strictNullChecks": true
}
tsconfig.json
diff
"compilerOptions": {
+ "strictNullChecks": true
}

3. Implement your appRouter

Follow the Quickstart to build your "hello world" router. Once you have your API implemented and listening via HTTP, you're ready to set up your React client.

Client Side

tRPC works great with Create React App!

1. Install dependencies

bash
npm install @trpc/client @trpc/server @trpc/react-query @tanstack/react-query
bash
npm install @trpc/client @trpc/server @trpc/react-query @tanstack/react-query
Why @trpc/server?

@trpc/server is a peer dependency of @trpc/client so you will need to make sure that it is available on your client!

Why @tanstack/react-query?

@trpc/react-query provides a thin wrapper over @tanstack/react-query. It is required as a peer dependency.

2. Create tRPC hooks

Create a set of strongly-typed React hooks from your AppRouter type signature with createTRPCReact.

utils/trpc.ts
tsx
// utils/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../path/to/router.ts';
export const trpc = createTRPCReact<AppRouter>();
utils/trpc.ts
tsx
// utils/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../path/to/router.ts';
export const trpc = createTRPCReact<AppRouter>();

3. Add tRPC providers

App.tsx
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import React, { useState } from 'react';
import { trpc } from './utils/trpc';
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: 'http://localhost:5000/trpc',
// optional
headers() {
return {
authorization: getAuthCookie(),
};
},
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
</trpc.Provider>
);
}
App.tsx
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import React, { useState } from 'react';
import { trpc } from './utils/trpc';
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: 'http://localhost:5000/trpc',
// optional
headers() {
return {
authorization: getAuthCookie(),
};
},
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{/* Your app here */}
</QueryClientProvider>
</trpc.Provider>
);
}
note

The reason behind the useState calls for the creation of the queryClient and the TRPCClient in the example above as opposed to declaring them outside of the component is to ensure that each request gets a unique client while using SSR so that request caches aren't shared between them.

4. Fetch data

pages/IndexPage.tsx
tsx
import { trpc } from '../utils/trpc';
export default function IndexPage() {
const hello = trpc.hello.useQuery({ text: 'client' });
if (!hello.data) return <div>Loading...</div>;
return (
<div>
<p>{hello.data.greeting}</p>
</div>
);
}
pages/IndexPage.tsx
tsx
import { trpc } from '../utils/trpc';
export default function IndexPage() {
const hello = trpc.hello.useQuery({ text: 'client' });
if (!hello.data) return <div>Loading...</div>;
return (
<div>
<p>{hello.data.greeting}</p>
</div>
);
}