A modern, production-ready Next.js boilerplate designed for high-fidelity SaaS applications. This starter implements a modular feature-slice architecture, a hexagonal authentication layer, and a configuration-driven UI system. Maintained and specialized by Good Shepherd Insights, LLC. for rapid application development.
This application follows a configuration-driven, modular architecture. For a deep-dive into the technical specifications, class relationships, and database ER diagrams, refer to the Full Architectural Specification.
The UI and business logic are decoupled via a central discovery registry. This allows features to be toggled or swapped without modifying the root layout or navigation components.
graph TD
subgraph "Core Registry"
Registry["FeatureRegistry (Singleton)"]
Activator["src/config/features-index.ts"]
end
subgraph "Feature Slices"
F_Dash["Dashboard Feature"]
F_Auth["Auth Feature"]
F_Custom["New Feature"]
end
F_Dash -- "Metadata" --> Activator
F_Auth -- "Metadata" --> Activator
F_Custom -- "Metadata" --> Activator
Activator -- "register()" --> Registry
Layout["app/layout.tsx"] -- "Discovery" --> Registry
Nav["Sidebar/Navbar"] -- "Query" --> Registry
To add a new feature to the system, follow the standardized "Plug-and-Play" workflow:
- Create Slice: Build your domain logic in
src/features/[feature-name]/. - Define Metadata: Create a
registry.tsin your slice that implements theFeatureMetadatainterface. - Activate: Import and add your feature to the list in
src/config/features-index.ts.
Detailed implementation rules are covered in ARCHITECTURE.md Section 4: Feature Plugin System.
Identity management is isolated behind a Port/Adapter boundary (src/auth/). This prevents the application core from being "locked in" to the Better Auth implementation.
- Port:
src/auth/types.tsdefines the interface. - Adapter:
src/auth/adapters/better-auth/handles the implementation. - Injection:
src/auth/server-provider.tsandclient-provider.tsexpose the session logic to the app.
For visual mapping of these boundaries, see ARCHITECTURE.md Section 3: Authentication Layer.
The codebase is organized to ensure features are self-contained and easily pluggable.
src/
├── app/ # Next.js App Router (Routing Shell)
├── auth/ # Hexagonal Auth Layer (Ports/Adapters/DI)
├── config/ # Platform-level feature activation index
├── db/ # Data persistence (Schema & Drizzle config)
├── design-systems/ # Unified UI primitives (shadcn, Radix, Tailwind v4)
├── features/ # Modular domain slices
│ ├── auth/ # Identity and Credential management
│ ├── dashboard/ # Performance overview and layout logic
│ ├── marketing/ # Public-facing landing and presentation
│ ├── new-dashboard/ # Analytical dashboard implementation
│ └── user-management/ # Administrative controls and RBAC actions
├── hooks/ # Global React hooks
├── lib/ # Core Registry and shared utility logic
└── proxy.ts # Middleware authentication shim
The starter provides a comprehensive feature set for building multi-tenant SaaS applications with strict domain separation.
- Multi-Provider Auth: Native support for Email/Password, GitHub, and Google OAuth.
- Account Linking: Automatic linking of multiple social identities to a single user profile.
- Email Verification: Mandatory verification flow via Resend integration.
- Role-Based Access Control: Domain-level RBAC (admin/user) with session-guarded layouts.
- Security Headers: Production-ready CSRF and Trusted Origin protection.
- Modular Dashboard: Configurable widget-based dashboard overview.
- User Management: Administrative interface for viewing, banning, and managing user roles.
- Registry System: Plugin-driven architecture for enabling/disabling feature slices.
- Theme System: OKLCH-based design tokens for perfectly consistent color scaling.
- Typography: Optimized Geist-compliant font stack (Zinc/Stone palette).
- Responsive Primitives: Pure Radix UI components with CVA variant logic.
Required secrets and configuration keys defined in .env.local.
| Variable | Required | Description |
|---|---|---|
| BETTER_AUTH_SECRET | Yes | High-entropy 32-character string used for cryptographic signing and encryption of session cookies. Generate via openssl rand -hex 32. Rotating this key will immediately invalidate all active user sessions. |
| BETTER_AUTH_URL | Yes | The canonical base URL of the application. In production, this must explicitly include the https:// protocol and match the public domain. Do not include a trailing slash. |
| DATABASE_URL | Yes | Full PostgreSQL connection URI. Format: postgresql://user:password@host:port/dbname?sslmode=require. Use sslmode=require to ensure encrypted transport for remote/managed production databases. |
| GITHUB_CLIENT_ID | No | The Client ID for the GitHub OAuth application. Registered under Developer Settings > OAuth Apps. |
| GITHUB_CLIENT_SECRET | No | The Client Secret for the GitHub OAuth application. Required for the access_token exchange flow. |
| GOOGLE_CLIENT_ID | No | The Client ID for the Google OAuth 2.0 application. Managed via the Google Cloud Console (APIs & Services). |
| GOOGLE_CLIENT_SECRET | No | The Client Secret for the Google OAuth application. The redirect URI must be authorized as ${BETTER_AUTH_URL}/api/auth/callback/google. |
| RESEND_API_KEY | No | API Key for transactional email delivery via Resend. Mandatory if requireEmailVerification is enabled in the auth configuration. |
The project uses Drizzle ORM for type-safe schema management and migrations.
- Generate Migrations:
pnpm db:generateto introspectsrc/db/schema.tsand create SQL files. - Apply Migrations:
pnpm db:migrateto push changes to the PostgreSQL instance. - Data Viewer:
pnpm db:studioto launch the local Drizzle Studio GUI.
To grant administrative access to a user account, use the Drizzle Studio interface or a SQL update:
UPDATE "user" SET role = 'admin' WHERE email = 'your-email@example.com';Once updated, the user will be granted access to the /admin workspace and management tools.
The stack is curated for maximum type safety, performance, and developer ergonomics.
- Framework: Next.js 16 (App Router)
- Authentication: Better Auth (Hexagonal Implementation)
- Database: PostgreSQL with Drizzle ORM
- Styling: Tailwind CSS v4 (Pure CSS Engine with OKLCH tokens)
- UI Components: Radix UI with shadcn/ui primitives
- Registry: Custom FeatureRegistry for dynamic discoverability
- Validation: Zod (End-to-end schema integrity)
- Deployment: Vercel ready
The identity management layer implements a strict Dependency Inversion Principle. By isolating the authentication SDK behind the IAuthServerAdapter and IAuthClientAdapter port interfaces (src/auth/), the application core remains decoupled from the specific implementation details of the underlying identity provider. This architecture allows for seamless provider swaps, sophisticated unit testing via mock adapters, and multi-environment flexibility—ensuring that the business logic never leaks into the authentication implementation.
The platform utilizes a Singleton-based Feature Registry model. Feature slices (src/features/) are self-describing modules that register their metadata—including navigation, widgets, and RBAC rules—with a central discovery engine at boot time. This facilitates Plug-and-Play modularity, where features can be enabled, disabled, or strictly gated by the multi-tenant engine simply by modifying the src/config/features-index.ts activator, requiring zero manual updates to the shared layout or navigation primitives.
The codebase follows a modular slicing strategy where domain-specific logic, components, and state are colocated within their respective feature directories. This prevents the emergence of "God Components" and ensures that the system scales linearly. Cross-feature dependencies are strictly managed via the public registry.ts interface, maintaining a clean directed acyclic graph (DAG) across the application architecture.
- Node.js 18 or higher
- PostgreSQL instance (Local or Supabase)
- Resend API key (for email verification)
- Clone the repository:
git clone https://github.com/good-shepherd-insights/multi-tenant-saas-starter - Install dependencies:
pnpm install - Configure environment:
cp .env.example .env.local - Populate database:
pnpm db:generatethenpnpm db:migrate - Launch:
pnpm dev
pnpm dev: Start development server with Turbopack (Rapid compilation)pnpm build: Compile for productionpnpm start: Launch production serverpnpm db:studio: Open Drizzle local data viewerpnpm lint: Run ESLint checks
Maintained by Good Shepherd Insights. Originally created by Zexa - better-auth-starter