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 packageA few notes beyond what the tree above already shows:
apps/emailis a thin React Email preview runner (email dev) — it has no localapp/oremails/dir; the templates live in@repo/email(packages/email/templates/), which this app points at.apps/web/middleware.tsstaysmiddleware.tsrather than Next.js 16'sproxy.tsconvention, becauseproxy.tsis 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/authimplements dual RBAC: platform-level admin roles plus separate organization-scoped roles. Stripe billing lives here too (there is nopackages/payment).- Root
docs/is this site (kit-docs, port 3006).apps/docsis the empty product-docs starter (port 3004).pnpm setupdeletes rootdocs/.
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)/andauth/. It is a post-login redirect hop with no UI: itredirects 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 onlyThere is no separate
/pricingroute — 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/webhookthrough the@better-auth/stripeplugin.
Import Path Aliases
Web App (apps/web/tsconfig.json)
"@/*" → apps/web/ // Web-specific files
"@repo/*" → packages/ // Shared packagesOther Apps/Packages
"@/*" → apps/[app-name]/ // App-specific files
"@repo/*" → packages/ // Shared packagesConfiguration Files
| File | Purpose |
|---|---|
turbo.json | Turborepo build orchestration |
pnpm-workspace.yaml | pnpm monorepo configuration |
tsconfig.json | Root TypeScript configuration |
biome.jsonc | Linting and formatting rules |
CLAUDE.md | Claude Code guidelines |