webdev.complete
šŸ—ļø The T3 Stack — Putting It Together
šŸš€Next.js & T3
Lesson 100 of 117
20 min

T3 Stack Overview

Theo's stack. The three pillars: types, auth, data.

Theo Browne (the "T") and a handful of collaborators looked at how full-stack TypeScript apps actually get built and carved out a short, opinionated list of tools that play well together. The T3 Stack is that list. It's not a framework. It's not a CMS. It's a curated bundle of well-known libraries plus a CLI that wires them up correctly the first time, so you stop debating import order and start writing features.

The bundle

Every T3 app starts with these six choices:

  • Next.js for the framework. App Router by default in current versions.
  • TypeScript, strict, with no escape hatches in the generated config.
  • Tailwind CSS for styling. No component library, just utility classes.
  • tRPC for the API. End-to-end types between server and client.
  • Prisma or Drizzle for the ORM. Drizzle is the newer recommendation but Prisma remains fully supported.
  • Auth.js (the renamed NextAuth.js) for authentication, or you can opt for Better Auth in newer T3 setups.

Plus Zod, which gets used everywhere there's an input or environment variable to validate.

The acronym evolves
Originally T3 stood for Theo (the person), TypeScript, and Tailwind. Now people sometimes call it the "Theo's Stack" or just "the T3 Stack." The CLI create-t3-app is the canonical entry point.

The three pillars

Every decision in the stack maps to one of three goals: types, auth, and data.

Pillar 1: types end to end

TypeScript strict mode is non-negotiable. Zod validates anything crossing a runtime boundary (input from forms, env vars, API bodies). tRPC carries those types across the network without codegen. Prisma/Drizzle generates DB types from your schema. The result: when you rename a column in the database, the failing builds tell you exactly which page to edit.

Pillar 2: auth that works in five minutes

Auth is the place greenfield projects bog down. T3 gives you a pre-wired Auth.js setup with a session callback that exposes the user ID, a Prisma/Drizzle adapter, and a typed auth() helper for Server Components and Server Actions. Add a provider (GitHub, Google, Discord) by uncommenting a block. You can swap to Better Auth for more modern primitives, but the shape is similar.

Pillar 3: data without the API design tax

Prisma or Drizzle define your schema in one file. tRPC procedures wrap that for client consumption. There's no REST route to design, no GraphQL schema to draft, no DTO layer. The same ctx.db you use in a script works in a server component, works in a tRPC procedure, works in a Server Action. One database client, many entry points.

What T3 explicitly does NOT include

Theo is loud about what the stack leaves out, and the omissions are intentional.

  • No state management library. No Redux, no Zustand, no Jotai. React state plus React Query (via tRPC) is enough for the apps T3 is aimed at.
  • No UI component library.No MUI, no Chakra, no Mantine. Tailwind plus headless primitives (Radix, shadcn) when you need them, but those are your call, not the stack's.
  • No CSS-in-JS. Tailwind handles styling. Runtime CSS-in-JS is incompatible with React Server Components and adds size.
  • No testing library preference.The stack doesn't pick Vitest vs Jest vs Playwright. You pick.
  • No monorepo.Single Next app by default. If you want Turborepo, that's a separate decision (and something Vercel encourages via next-forge for larger teams).
The point of the omissions
T3 is shaped by "what do we ALL agree we want?" If smart people disagree about a choice (UI lib, state lib, test runner), the stack doesn't pick. It leaves the slot open and gives you the rest pre-wired.

The directory shape you'll see

bash
my-t3-app/
ā”œā”€ā”€ prisma/
│   └── schema.prisma           # or drizzle/schema.ts
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ app/                    # Next.js App Router
│   │   ā”œā”€ā”€ layout.tsx
│   │   ā”œā”€ā”€ page.tsx
│   │   └── api/
│   │       ā”œā”€ā”€ auth/[...nextauth]/route.ts
│   │       └── trpc/[trpc]/route.ts
│   ā”œā”€ā”€ server/
│   │   ā”œā”€ā”€ api/
│   │   │   ā”œā”€ā”€ root.ts         # appRouter
│   │   │   ā”œā”€ā”€ trpc.ts         # publicProcedure / protectedProcedure
│   │   │   └── routers/        # your procedures
│   │   ā”œā”€ā”€ auth.ts             # Auth.js config + helper
│   │   └── db.ts               # Prisma/Drizzle client
│   ā”œā”€ā”€ trpc/
│   │   ā”œā”€ā”€ react.tsx           # client provider + hooks
│   │   └── server.ts           # server caller for RSCs
│   └── env.js                  # Zod-validated env vars
ā”œā”€ā”€ .env.example
└── package.json

Two things to notice. First, every file has a single job, and the names are dull (in a good way). Second, the API/data layer lives under src/server/, mirrored on the client by src/trpc/. That symmetry shows up in every T3 app you'll ever read.

Why use T3 instead of rolling your own

  • You stop debating which auth library, which ORM, which fetcher. The choices are made; you can rewire later if you must.
  • env.js alone is worth the install. Missing environment variables fail at startup with a clear message, not silently at request time.
  • The generated project follows current best practices for the App Router. Outdated tutorials and Pages Router patterns are gone.
  • The community is sizeable: every feature you want to add (Stripe, file uploads, image storage) has a discussed solution.

Quiz

Quiz1 / 3

Which of these is NOT included by default in a T3 app?

Recap

  • T3 = Next.js + TypeScript + Tailwind + tRPC + Prisma/Drizzle + Auth.js (or Better Auth) + Zod.
  • Three pillars: types end-to-end, auth wired up, data via ORM + tRPC.
  • What it doesn't include is also part of the design: no state lib, no UI lib, no CSS-in-JS, no test runner pick, no monorepo.
  • env.js validates environment variables at startup. Forget one and the app refuses to boot.
  • Generate one with pnpm create t3-app@latest (the next lesson walks through it).
Built with Next.js, Tailwind & Sandpack.
Learn. Build. Ship.