Project Structure
The template follows a route-first structure in src/app, with supporting layers for configuration, shared UI, and backend services.
The goal of this layout is not only to keep files organized. It is to make the codebase easier to read, easier to extend, and easier to maintain as your product grows.
How to read this page
Start with the high-level map, then use the placement rules to decide where new code should live before you create files in the wrong layer.
High-level map
Why the structure matters
When each layer has a clear responsibility, it becomes easier to know where to add new code and where not to.
Routes stay focused
Pages, layouts, and route-local composition remain close to the app entry points instead of becoming a home for every kind of logic.
UI stays readable
Primitive components, shared components, and feature-specific pieces stay separated so the design system does not mix with product logic.
Configuration stays centralized
Brand, navigation, content, and integration defaults live in predictable config surfaces instead of being scattered through routes.
Business logic stays reusable
Server-side logic can evolve independently from pages and transport layers when it lives in services instead of inside route files.
Practical rule
If a piece of code can be reused outside a single route, it usually should not be buried inside a page file.
Placement rules
src/app
Use this layer for:
page.tsx,layout.tsx,loading.tsx,error.tsx- Route handlers in
app/api - Route-local UI composition
Avoid putting domain logic here when it can live in src/server/services.
src/components/ui
Use for unopinionated primitives and design-system level building blocks.
src/features
Use for reusable UI tied to a product area, such as marketing-specific components or auth forms.
src/content
Use for content collections and their schemas (e.g. blog posts, changelog entries).
src/server/api
Use for tRPC routers, procedure definitions, and transport-level concerns.
src/server/services
Use for business logic that should not be coupled to a route or a tRPC router.
Good separation
A route should decide what to render. A service should decide how a business process works. A router should decide how that behavior is exposed.
Examples in the current codebase:
src/server/services/billing/checkout.tssrc/server/services/billing/webhook-sync.tssrc/server/services/billing/customer.ts