Project Structure and Organization
Understand the monorepo structure, folder organization, and file naming conventions of the Next.js Prisma SaaS Kit.
The Next.js Prisma SaaS Kit is organized as a monorepo using Turborepo for build orchestration and pnpm workspaces for dependency management.
Why a Monorepo?
Benefits:
- Code Reusability - Share packages across multiple apps
- Type Safety - Shared types across the entire stack
- Atomic Changes - Update multiple packages in a single commit
- Consistent Tooling - Unified linting, formatting, and testing
- Better Developer Experience - Jump to definitions across packages
Structure:
next-prisma-saas-kit-turbo/├── apps/ # Applications├── packages/ # Shared packages├── tooling/ # Build and utility scripts├── turbo/ # Turborepo generators├── docs/ # Documentation├── turbo.json # Turborepo configuration├── pnpm-workspace.yaml # pnpm workspace configuration└── package.json # Root package.jsonTop-Level Structure
Apps Directory
Contains deployable applications:
apps/├── web/ # Main Next.js SaaS application├── dev-tool/ # Development tools for env vars and translations└── e2e/ # Playwright end-to-end testsapps/web - Main Application
- Next.js 16 with App Router
- All user-facing pages and features
- API routes and server actions
- Marketing website
apps/e2e - End-to-End Tests
- Playwright test suites
- Page object models
- Test utilities
Packages Directory
Contains reusable packages shared across apps.
Key Package Descriptions
@kit/auth - Authentication Components
- Sign-in/sign-up forms
- Password reset flows
- MFA components
- Auth utilities
@kit/better-auth - Auth Configuration
- Better Auth setup
- Session management
- Authentication providers
@kit/database - Database Layer
- Prisma ORM schema definitions
- Migration files
- Database utilities
- Type-safe query builders
@kit/ui - UI Component Library
- Shadcn UI components
- Form components
- Layout components
- Shared design system
@kit/organization-* - Organization Features (nested packages)
- Organization CRUD operations
- Member management
- Invitation system
- Role management
@kit/rbac - Authorization
- Permission definitions
- Role-based access control
- Policy enforcement
@kit/storage - File Storage
- File upload utilities
- Storage provider abstraction
- Image handling
@kit/email-templates - Email Templates
- Transactional emails
- React Email components
- Email layouts
Web App Structure
The main application (apps/web) follows Next.js App Router conventions:
apps/web/├── app/ # Next.js app directory│ ├── [locale]/ # Internationalized routes│ │ ├── auth/ # Authentication pages│ │ ├── (invitation)/ # Invitation pages│ │ ├── (public)/ # Public marketing pages│ │ ├── (internal)/ # Authentication protected pages│ ├── api/ # API routes│ └── layout.tsx # Root layout├── components/ # Shared React components├── config/ # App configuration├── i18n/ # Translation files│ └── messages/ # Locale-specific messages├── public/ # Static assets├── styles/ # Global styles├── .env # Public environment variables├── .env.local # Local secrets (git-ignored)├── prisma/ # Prisma schema in packages/database/src/prisma/├── next.config.ts # Next.js configuration└── package.json # DependenciesApp Directory Deep Dive
Route Groups (folders with parentheses):
(invitation)/- Invitation pages with specific layout(public)/- Public pages with marketing layout(internal)/- Authentication protected pages with specific layout- Regular folders become URL segments
Dynamic Routes:
[locale]/- Language/locale parameter
The full application routing is scoped by the [locale] parameter. This is optional for non-default locales (by default, the locale is en). Ex. /es/auth/sign-in (for Spanish) or /auth/sign-in (for English).
Special Files:
page.tsx- Page component (creates route)layout.tsx- Layout wrapperloading.tsx- Loading UIerror.tsx- Error boundarynot-found.tsx- 404 page
Please refer to the Next.js documentation for more information on the routing conventions.
Custom Conventions
Below are some conventions we use in the application.
*.schema.ts # Zod validation schemas*-server-actions.ts # Server actions*-page.loader.ts # Data loaders for pages*.config.ts # Configuration files*.types.ts # TypeScript type definitionsThese are just conventions; you are free to use your own naming conventions as long as you are consistent within your codebase.
Page Loaders
These are server-side functions that fetch data before a page is rendered. They are used to populate the page with data in React Server Components (RSC).
Server Actions
These are server-side functions that perform actions on the server. They are used to perform actions on the server in React Server Components (RSC).
API Routes
These are API routes that are used to perform server-side actions from the client-side.
Schemas
These are Zod schemas that are used to validate data both on the client-side and the server-side.
Please refer to the Zod documentation for more information on the Zod schemas.
Component Naming
Components are named in kebab-case.
Examples:
✅ Good:- user-profile-card.tsx- invite-member-form.tsx- get-user-session.ts- format-date.ts❌ Avoid:- UserProfileCard.tsx (PascalCase files)- InviteMemberForm.tsx- getUserSession.ts (camelCase for component files)Package Structure
Each package follows a similar structure:
packages/example/├── src/│ ├── index.ts # Public exports│ ├── components/ # React components (if any) ├── lib/ # Library code├── package.json├── tsconfig.json├── eslint.config.mjsTo create a ready-to-use package, you can run the following command:
pnpm turbo gen packageAnd follow the prompts.
Package Exports
Packages export through src/index.ts:
// packages/auth/src/index.tsexport { SignInForm } from './components/sign-in-form';export { useAuth } from './hooks/use-auth';export { getSession } from './utils/get-session';Usage in apps:
import { SignInForm, useAuth } from '@kit/better-auth/context';Make sure to always separate client and server exports so that you don't accidentally import server-code in client-side bundles.
package.json
{ "exports": { "./client": "./src/client.ts", "./server": "./src/server.ts" }}Development Patterns
Adding a New Page
- Create file in
apps/web/app/[locale]/[...]/page.tsx - Export default async function
- Add metadata export
- Optionally create loading and error boundaries
// apps/web/app/[locale]/my-page/page.tsximport type { Metadata } from 'next';export const metadata: Metadata = { title: 'My Page', description: 'Page description',};export default async function MyPage() { return <div>My Page Content</div>;}Adding a New Package
Run the command:
pnpm turbo gen packageAnd follow the prompts.
Best Practices
1. Keep Packages Focused
Each package should have a single responsibility:
- ✅
@kit/auth- Authentication only - ❌
@kit/auth-and-users-and-settings- Too broad
2. Avoid Circular Dependencies
Packages should not depend on each other circularly:
- ✅
auth→database←organizations - ❌
auth→organizations→auth
3. Export Only Public APIs
Only export what's meant to be used by other packages:
// ✅ Good - explicit exportsexport { SignInForm } from './components/sign-in-form';// ❌ Avoid - exporting everythingexport * from './components';4. Use Absolute Imports
Prefer workspace imports over relative imports across packages:
// ✅ Goodimport { Button } from '@kit/ui';// ❌ Avoidimport { Button } from '../../../packages/ui/src/components/button';5. Consistent File Naming
Stick to the naming conventions:
- Components:
kebab-case.tsx - Utilities:
kebab-case.ts - Server Actions:
*-server-actions.ts - Schemas:
*.schema.ts - Page Loaders:
*-page.loader.ts
Navigating the Codebase
Finding Code
By Feature:
- Authentication →
packages/auth/ - Database →
packages/database/ - UI Components →
packages/ui/ - Accounts →
packages/account/... - Organizations →
packages/organization/... - Billing →
packages/billing/... - Email Templates →
packages/email-templates - Mailers →
packages/mailers - RBAC →
packages/rbac - Storage →
packages/storage - Action Middleware →
packages/action-middleware
By Page:
- Landing Page →
apps/web/app/[locale]/(public)/page.tsx - Sign In →
apps/web/app/[locale]/auth/sign-in/page.tsx - Dashboard →
apps/web/app/[locale]/(internal)/dashboard/page.tsx - Admin →
apps/web/app/[locale]/(internal)/admin/page.tsx
Summary
Key Takeaways:
- Monorepo structure with
apps/andpackages/ - Turborepo for build orchestration
- pnpm workspaces for dependency management
- Consistent naming conventions
- Clear separation of concerns
- Type safety across the entire stack
Next Steps:
- Configuration - Customize the kit
- Development Guide - Start building features
Next: Configuration →