Every time I start a new Next.js project with tRPC, I do the same thing.
Open docs. Copy files. Install packages. Forget to wrap layout.tsx. Get the QueryClient error. Fix it. Repeat next project.
I got tired of it. So I built a CLI.
npx create-trpc-setup
The Problem
Setting up tRPC v11 with Next.js App Router is not hard — but it's tedious. You need:
- Install
@trpc/server,@trpc/client,@trpc/tanstack-react-query,@tanstack/react-query,zod,server-only... - Create
trpc/init.tswith context and procedures - Create
trpc/query-client.tswith SSR-safe QueryClient - Create
trpc/client.tsxwith TRPCReactProvider - Create
trpc/server.tsxwith HydrateClient and prefetch - Create
app/api/trpc/[trpc]/route.ts - Update
layout.tsxto wrap children with TRPCReactProvider
Miss any step → error. Every new project.
The Solution
npx create-trpc-setup
Run this inside any existing Next.js project. Everything happens automatically.
What gets detected automatically:
- Package manager — npm, pnpm, yarn, or bun
-
Path alias — reads
tsconfig.jsonfor@/*,~/*, or any custom alias - Auth provider — detects Clerk or NextAuth and configures context
-
Folder structure —
src/or root layout
What gets generated:
trpc/
├── init.ts ← context, baseProcedure, protectedProcedure, Zod error formatter
├── query-client.ts ← SSR-safe QueryClient
├── client.tsx ← TRPCReactProvider + useTRPC hook
├── server.tsx ← prefetch, HydrateClient, caller
└── routers/
└── _app.ts ← health + greet procedures with Zod
app/api/trpc/[trpc]/route.ts ← API handler with real headers
app/trpc-status/ ← test page (delete after confirming)
layout.tsx — auto-patched:
Before:
<body>
{children}
<Toaster />
</body>
After:
<body>
<TRPCReactProvider>
{children}
<Toaster />
</TRPCReactProvider>
</body>
Using tRPC After Setup
Server Component — prefetch data:
// app/page.tsx
import { HydrateClient, prefetch, trpc } from "@/trpc/server";
import { MyClient } from "./my-client";
export default function Page() {
prefetch(trpc.greet.queryOptions({ name: "World" }));
return (
<HydrateClient>
<MyClient />
</HydrateClient>
);
}
Client Component — use data:
// my-client.tsx
"use client";
import { useSuspenseQuery } from "@tanstack/react-query";
import { useTRPC } from "@/trpc/client";
export function MyClient() {
const trpc = useTRPC();
const { data } = useSuspenseQuery(trpc.greet.queryOptions({ name: "World" }));
return <div>{data.message}</div>;
}
Why Not create-t3-app?
create-t3-app is great — but it only works for new projects.
create-trpc-setup works with existing projects. Already have a Next.js app with Clerk, Shadcn, and custom providers? No problem. Run the command, everything gets added without touching your existing code.
protectedProcedure — Already Included
The generated init.ts includes a protectedProcedure that automatically throws UNAUTHORIZED:
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.userId) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({ ctx: { ...ctx, userId: ctx.userId } });
});
Use it in any router:
getProfile: protectedProcedure.query(({ ctx }) => {
return { userId: ctx.userId }; // guaranteed non-null
}),
Try It
npx create-trpc-setup
If it saved you time, drop a ⭐ on GitHub and share it with your team.
Top comments (0)