Project structure
The layout
src/
app/[locale]/ # All pages, localized
(auth)/ # Sign-in, sign-up, verification, password reset…
(marketing)/ # Public: home, pricing, changelog, legal
(app)/ # Authenticated shell (sidebar)
(org)/ # Org-scoped: dashboard, settings, billing, members, credits
(user)/ # User-scoped: account profile, security, sessions
admin/ # Platform admin
api/ # Auth, tRPC, webhooks, storage route handlers
config/ # Feature flags (*.config.ts)
server/ # tRPC — server-only
routers/ # One file per domain
procedures.ts # Procedure tiers + middleware
lib/ # auth, billing, payment, email, storage, audit, db, logger
components/
ui/ # The kit's own unstyled component library
icons/ # Curated icon re-export
[feature]/ # Feature components (auth/, billing/, org/…)
emails/ # React Email templates
trpc/ # Client bindings + server caller
env.ts # Zod-validated env — single source of truth
i18n/ # Routing, request config, navigation helpers
messages/ # en.json, es.json
prisma/ # schema.prisma, migrations, seed
The path alias @/* maps to src/*.
Conventions that matter
- Files and folders are
kebab-case. React component files arePascalCasematching their export (UserAvatar.tsx). .server.tssuffix marks modules that must never reach a client bundle (Prisma access, secrets, email sending).- Route groups
(auth),(app),(org)organize without affecting the URL. - Feature components go in
src/components/[feature]/, never insidesrc/components/ui/.
Boundaries the tooling enforces
Some rules are enforced by ESLint, not just convention:
- Prisma is only imported inside
src/server/routers/. Components, pages, lib utilities, and config never touch the database directly. - Feature code imports UI from
@/components/ui, neverradix-uidirectly. - Icons come from
@/components/icons, neverlucide-reactdirectly. - All code reads env from
@/env, neverprocess.env.
These boundaries are what keep the layers swappable and the data access safe — see The tRPC API and UI & styling.