saasprokit
Getting Started

Project Structure

Overview of the monorepo directory layout and organization

Directory Structure

saasprokit/
├── apps/                          # Independent applications
│   ├── web/                       # Main SaaS application (port 3000)
│   │   ├── app/                   # Next.js app directory
│   │   │   ├── [locale]/          # Locale routing
│   │   │   │   ├── (app)/         # Authenticated app routes
│   │   │   │   │   ├── [orgSlug]/ # Organization-scoped routes
│   │   │   │   │   ├── account/   # User account pages
│   │   │   │   │   ├── admin/     # Platform admin (users, organizations)
│   │   │   │   │   └── invite/    # Organization invitation handling
│   │   │   │   ├── (frontier)/    # Public/marketing routes
│   │   │   │   ├── auth/          # Authentication flows
│   │   │   │   └── org/           # Post-login redirect hop (no UI; outside (app))
│   │   │   ├── api/               # API routes
│   │   │   └── layout.tsx         # Root layout
│   │   ├── components/            # Shared app components
│   │   ├── lib/                   # Server data-access layer (auth, org, billing, files, admin)
│   │   ├── public/                # Static assets
│   │   ├── middleware.ts          # Edge middleware (cookie-presence route gate)
│   │   └── package.json
│   │
│   ├── docs/                      # Customer's product docs — empty Fumadocs starter (port 3004)
│   │   ├── app/                   # Fumadocs app
│   │   │   ├── (home)/            # Landing page
│   │   │   ├── docs/              # Documentation pages
│   │   │   └── api/               # Search API
│   │   ├── content/               # Markdown documentation
│   │   └── package.json
│   │
│   ├── email/                     # React Email preview runner (port 3003)
│   │   └── package.json           # `email dev` against packages/email/templates
│   │
│   ├── storybook/                 # Component library (port 6006)
│   │   ├── stories/               # Component stories
│   │   └── package.json
│   │
│   └── studio/                    # Prisma Studio wrapper (port 3005)
│       └── package.json

├── docs/                          # SaaSProKit's own docs — kit-docs (port 3006)
│   ├── content/docs/              # MDX pages (this site)
│   ├── app/                       # Fumadocs app
│   └── package.json               # workspace name `kit-docs`; excluded from `pnpm dev` / `pnpm build`

├── packages/                      # Shared packages
│   ├── auth/                      # Better Auth, dual RBAC & Stripe wiring
│   │   ├── client.ts              # Client-side auth hooks
│   │   ├── server.ts              # Server-side auth instance
│   │   ├── permissions.ts         # RBAC definitions
│   │   ├── keys.ts                # Validated env schema
│   │   ├── plans.ts, helpers.ts, guards.ts  # Tiers, status helpers, downgrade guard
│   │   ├── stripe-hooks.ts        # Stripe webhook/event handlers (@better-auth/stripe)
│   │   ├── stripe-client.ts       # Stripe client
│   │   ├── billing-authorize.ts   # Billing permission checks
│   │   ├── index.ts               # Package `main` (prefer deep imports: `@repo/auth/server`)
│   │   └── package.json
│   │
│   ├── database/                  # Prisma ORM & schema
│   │   ├── prisma/
│   │   │   ├── schema.prisma      # Data models
│   │   │   └── seed.ts            # Database seed script
│   │   ├── generated/
│   │   │   └── client/            # Generated Prisma client
│   │   ├── prisma.config.ts       # Prisma configuration
│   │   └── package.json
│   │
│   ├── design-system/             # shadcn/ui + app UI components
│   │   ├── components/
│   │   │   ├── ui/                # shadcn base components (vendored)
│   │   │   ├── molecules/         # Composite components (e.g. molecules/error/*)
│   │   │   ├── blocks/            # Page/section blocks
│   │   │   ├── layout/            # Layout components
│   │   │   ├── data-table/        # Data table
│   │   │   └── icons/             # Icons
│   │   ├── hooks/                 # use-mobile, use-toast
│   │   ├── providers/             # Theme + design-preset providers
│   │   ├── lib/
│   │   │   └── utils.ts           # Utility functions (cn, etc)
│   │   ├── styles/
│   │   │   └── globals.css        # Global styles
│   │   └── package.json
│   │
│   ├── email/                     # React Email templates + Resend
│   │   ├── templates/             # Email templates (.tsx)
│   │   ├── components/, providers/, i18n/
│   │   └── package.json
│   │
│   ├── utils/                     # Shared utilities
│   │   ├── slug.ts, date.ts       # Slug + date helpers
│   │   ├── user.ts                # Password constants (MIN/MAX_PASSWORD_LENGTH)
│   │   ├── organization.ts, payment.ts, storage.ts, auth.ts
│   │   └── package.json
│   │
│   ├── logger/                    # Console-backed structured logging
│   │   ├── log.ts                 # @repo/logger/log
│   │   └── package.json
│   │
│   ├── seo/                       # SEO utilities
│   │   └── package.json
│   │
│   ├── internationalization/      # i18n & localization
│   │   └── package.json
│   │
│   ├── storage/                   # File upload & storage
│   │   └── package.json
│   │
│   └── typescript-config/         # Shared TypeScript config
│       └── base.json

├── turbo.json                     # Turborepo configuration
├── pnpm-workspace.yaml            # pnpm workspaces
├── tsconfig.json                  # Root TypeScript config
├── biome.jsonc                    # Biome linting config
├── CLAUDE.md                      # Claude Code guidelines
└── package.json                   # Root package

A few notes beyond what the tree above already shows:

  • apps/email is a thin React Email preview runner (email dev) — it has no local app/ or emails/ dir; the templates live in @repo/email (packages/email/templates/), which this app points at.
  • apps/web/middleware.ts stays middleware.ts rather than Next.js 16's proxy.ts convention, because proxy.ts is Node-runtime-only and can't run on the Cloudflare/OpenNext deploy target. It does an optimistic, cookie-presence-only route gate — see Sessions & Route Protection.
  • @repo/auth implements dual RBAC: platform-level admin roles plus separate organization-scoped roles. Stripe billing lives here too (there is no packages/payment).
  • Root docs/ is this site (kit-docs, port 3006). apps/docs is the empty product-docs starter (port 3004). pnpm setup deletes root docs/.

Route Structure in Web App

Authenticated Routes (/[locale]/(app)/)

/[locale]/(app)/
├── [orgSlug]/          # Organization-scoped routes
│   ├── page.tsx        # Organization dashboard
│   ├── settings/       # Organization settings
│   ├── billing/        # Billing management
│   ├── members/        # Member management
│   └── checkout/       # Checkout flow
├── account/            # User account routes (no org required)
│   ├── profile/        # Profile settings
│   ├── security/       # Security settings (sessions, 2FA)
│   ├── danger/         # Account deletion
│   └── new-org/        # Create organization
├── admin/              # Platform admin (requireAdminArea)
│   ├── users/
│   └── organizations/
└── invite/             # Organization invitation handling

/[locale]/org/ is a sibling of (app)/, not nested inside it — it sits outside the (app) route group at the [locale]/ level, alongside (frontier)/ and auth/. It is a post-login redirect hop with no UI: it redirects to the default org or /account/new-org.

Public Routes (/[locale]/(frontier)/)

/[locale]/(frontier)/
├── page.tsx           # Home/landing page (Hero, Features, Pricing from `@repo/design-system/components/blocks/*`)
└── components/        # Header user-menu only

There is no separate /pricing route — pricing is a block on the home page.

Authentication Routes (/[locale]/auth/)

Each route is a Server-Component page.tsx plus a components/ folder of "use client" logic; auth form UI is colocated there too (submission via server actions + useActionState).

/[locale]/auth/
├── components/       # shared: auth-card.tsx, social-providers.tsx
├── schemas.ts        # shared zod schemas for every auth form
├── actions.ts        # signInSocialAction (shared by sign-in and sign-up)
├── sign-in/          # page.tsx + components/{sign-in-form,magic-link-sign-in,email-otp-sign-in}.tsx
├── sign-up/          # page.tsx + components/sign-up-form.tsx
├── forgot-password/  # page.tsx + components/
├── reset-password/   # page.tsx + components/
├── verify-email/     # page.tsx + components/
└── 2fa/              # page.tsx + components/

Magic link sign-in is a component on the sign-in/ page, not its own route.

API Routes (/api/)

/api/
├── auth/[...all]/    # Better Auth handler (incl. Stripe webhooks via @better-auth/stripe)
└── health/           # Health check (database + auth)

Billing (checkout / change-plan) and invitations are handled by server actions ((app)/[orgSlug]/.../actions.ts), not REST routes. Stripe webhooks arrive at /api/auth/stripe/webhook through the @better-auth/stripe plugin.

Import Path Aliases

Web App (apps/web/tsconfig.json)

"@/*" → apps/web/         // Web-specific files
"@repo/*" → packages/     // Shared packages

Other Apps/Packages

"@/*" → apps/[app-name]/  // App-specific files
"@repo/*" → packages/     // Shared packages

Configuration Files

FilePurpose
turbo.jsonTurborepo build orchestration
pnpm-workspace.yamlpnpm monorepo configuration
tsconfig.jsonRoot TypeScript configuration
biome.jsoncLinting and formatting rules
CLAUDE.mdClaude Code guidelines

On this page