Stop manually setting up tRPC in Next.js — use this CLI instead
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. Every project needs:
- 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 - Manually update
layout.tsxto wrap<body>with TRPCReactProvider
Miss any step → error. Every. Single. Project.
The Solution
npx create-trpc-setup
Run this inside any existing Next.js project. Everything happens automatically.
What gets auto-detected:
- Package manager — npm, pnpm, yarn, or bun
- Path alias — reads
tsconfig.jsonfor@/*,~/*, or any custom alias - Auth provider — detects Clerk or NextAuth, configures context automatically
- Folder structure —
src/or root layout
Files 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
└── routers/
└── _app.ts ← starter router with health + greet (Zod validation)
app/api/trpc/[trpc]/
└── route.ts ← API handler with real headers + dev error logging
app/trpc-status/ ← styled test page (delete after confirming ✅)
layout.tsx — auto-patched
Before:
<body>
{children}
<Toaster />
</body>
After:
<body>
<TRPCReactProvider>
{children}
<Toaster />
</TRPCReactProvider>
</body>
No backup files. No extra clutter. Just the import added + body wrapped.
Why Not create-t3-app?
create-t3-app is great — but it only works for new projects.
create-trpc-setup works with projects you've already started.
| Feature | create-t3-app | create-trpc-setup |
|---|---|---|
| New projects | ✅ | ✅ |
| Existing projects | ❌ | ✅ |
| Auto-patches layout.tsx | ❌ | ✅ |
| Clerk / NextAuth detection | ❌ | ✅ |
| tsconfig path alias detection | ❌ | ✅ |
| tRPC v11 + RSC ready | ✅ | ✅ |
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>;
}
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", message: "Sign in required" });
}
return next({ ctx: { ...ctx, userId: ctx.userId } });
});
Use it directly in any router:
getProfile: protectedProcedure.query(({ ctx }) => {
return { userId: ctx.userId }; // guaranteed non-null ✅
}),
Zod Error Formatter — Also Included
Clean field-level errors on the client:
error?.data?.zodError?.fieldErrors?.title // ["Title is required"]
Try It
npx create-trpc-setup
- 📦 npm: https://www.npmjs.com/package/create-trpc-setup
- 🐙 GitHub: https://github.com/Dhavalkurkutiya/create-trpc-setup
If this saved you time, drop a ⭐ on GitHub — it helps others find it!
Top comments (0)