This is a very opinionated template that i initially made for myself to get started on prototypes quicker. I released it as a GitHub template, because i thought that others might find it useful.
it's featuring a full landing / auth / protected dashboard, including email integrations (resend), multi-tenancy, notification system and documentation system. it's made with a bunch of instructions for LLM's to easily prototype with the system.
- Marketing site with responsive nav, hero, feature grid/carousel, and footer
- Auth system powered by NextAuth v5 (credentials + optional OAuth)
- Three login modes:
emailAndPassword,emailOTP, ornone - Optional required email confirmation before credentials sign-in
- Password reset flow (request reset link + token-based password update)
- Email OTP flow with automatic user creation on first successful code login
- MongoDB integration for users/auth data (via
@auth/mongodb-adapter) - Resend integration for confirmation emails, OTP codes, and reset links
- Multi-tenant organizations with automatic personal org creation for new users
- Organization member management (invite, role updates, remove/leave safeguards)
- Permission-based access helpers for both server and client components
- In-app notification system with per-user delivery state + invitation notifications
- Protected dashboard route groups with custom sidebars and a shared app shell
- Config-driven rail navigation, footer links/socials, branding, and settings sections
- Config-driven MDX documentation area with generated sidebar tree + in-page table of contents
- Reusable UI system based on shadcn + Base UI + HugeIcons + Tailwind CSS v4
- Dark mode support via
next-themes - TypeScript strict mode + ESLint core-web-vitals config
- Prebuilt error experiences (
403,404,500) and global error boundary
- User API keys
This repository is a Next.js App Router starter for SaaS products. It gives you:
- a public-facing landing page (
/) - auth pages (
/login,/register,/forgot-password,/reset-password) - authenticated dashboard sections (
/overview,/projects,/pages,/docs, etc.) - centralized project customization through
config.ts
- Next.js
16.1.6(App Router) - React
19.2.3 - TypeScript
strict: true - Tailwind CSS v4
- shadcn/ui + Base UI primitives
- HugeIcons
- NextAuth
5.0.0-beta.30 - MongoDB +
@auth/mongodb-adapter - Framer Motion
next-mdx-remote+remark-gfmbcryptjs
- Node.js version compatible with Next.js 16 (current LTS recommended)
pnpm- MongoDB instance (local or cloud)
- Resend account/API key if you use any email-delivered auth flows
- Install dependencies.
pnpm install- Create local environment file.
cp .env.example .env.local-
Fill required environment variables (see full matrix below).
-
Run development server.
pnpm dev- Open
http://localhost:3000.
pnpm dev- start local dev serverpnpm build- production buildpnpm start- run production serverpnpm lint- run ESLint
| Variable | Required | Purpose |
|---|---|---|
MONGODB_URI |
Yes | Required at startup. Missing value throws immediately from lib/mongodb.ts. |
NEXTAUTH_SECRET |
Yes | Secret for NextAuth JWT/session security. |
NEXTAUTH_URL |
Recommended | NextAuth base URL, especially important in production. |
NEXT_PUBLIC_APP_URL |
Recommended | Used by config.ts as siteConfig.url; drives metadata base URL and email link generation. Defaults to http://localhost:3000. |
RESEND_API_KEY |
Conditional | Required for email confirmation, email OTP, and forgot-password email delivery. |
RESEND_FROM_EMAIL |
No | Custom sender address. Falls back to ${siteConfig.name} <noreply@mail.skxv.dev>. |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
Optional | Enables Google OAuth button/provider. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
Optional | Enables GitHub OAuth button/provider. |
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRET |
Optional | Enables Discord OAuth button/provider. |
TWITTER_CLIENT_ID / TWITTER_CLIENT_SECRET |
Optional | Enables Twitter/X OAuth button/provider. |
siteConfig.auth.genericLoginType |
Credentials Login | OTP Login | Register Form | Resend Needed? |
|---|---|---|---|---|
emailAndPassword |
Yes | No | Enabled | Only if requireEmailConfirmation=true or using forgot-password |
emailOTP |
No password login | Yes (/api/auth/request-email-otp) |
Disabled (users auto-created on first OTP success) | Yes |
none |
Disabled | Disabled | Disabled | No (unless you use custom email features) |
When siteConfig.auth.requireEmailConfirmation is true:
- Email/password sign-in blocks unverified users in
lib/auth.ts - Registration requires
RESEND_API_KEY; otherwise/api/auth/registerreturns503 - Confirm email link uses
/api/auth/confirm-email?token=... - Login form exposes a "Resend confirmation email" action after failed credentials login
Provider wiring happens in two places:
lib/auth.config.ts: base provider list + route authorization callbacklib/auth.ts: injects real credentials provider depending ongenericLoginType, and keeps OAuth providers fromauth.config
If both credentials and OAuth are effectively disabled, a no-op credentials provider is added to keep NextAuth configuration valid.
Protected route logic lives in callbacks.authorized inside lib/auth.config.ts.
Protected prefixes:
/overview/lists/tables/documentation/documents/messages/guides/resources/projects/pages/docs
Auth routes (redirect authenticated users to /overview):
/login/register/forgot-password/reset-password
Middleware matcher in middleware.ts excludes API routes, Next static/image assets, favicon.ico, /assets, and file-extension requests.
| Endpoint | Method | Purpose | Important Rules |
|---|---|---|---|
/api/auth/[...nextauth] |
GET/POST |
NextAuth handlers | Exported from lib/auth.ts. |
/api/auth/register |
POST |
Create email/password user | Only active in emailAndPassword mode; password min length 8; optional email confirmation flow. |
/api/auth/confirm-email |
GET |
Verify confirmation token | Consumes token, sets users.emailVerified, redirects to login with query status. |
/api/auth/resend-confirmation |
POST |
Re-issue confirmation email | Requires requireEmailConfirmation=true and RESEND_API_KEY. |
/api/auth/forgot-password |
POST |
Send password reset link | Only in emailAndPassword; returns generic success message to avoid account enumeration. |
/api/auth/reset-password |
POST |
Apply new password | Requires valid unexpired reset token + password length >= 8. |
/api/auth/request-email-otp |
POST |
Send 6-digit OTP code | Only in emailOTP; requires RESEND_API_KEY. |
- Tokens are SHA-256 hashed before storage
- Any existing token of same
email + typeis deleted before issuing a new one - Expiry durations:
- Email confirmation token: 24 hours
- Password reset token: 60 minutes
- Email OTP: 10 minutes
- Tokens are single-use (consumed then deleted)
- Email links are built from
siteConfig.url - If
siteConfig.urlis wrong in production, confirmation/reset links will point to the wrong origin
usersauth_tokens
Fields used by app logic:
nameemailpassword(for credentials users)emailVerifiedimagecreatedAtupdatedAt
In emailOTP mode, a missing user is created after successful OTP verification with:
namederived from email local-partemailVerifiedset immediately
This template includes a practical multi-tenant setup out of the box:
- users belong to one or more organizations
- each user has a
currentOrganizationId - each user automatically gets a personal "home" organization on onboarding/sign-up
- invitations can be sent to add users to organizations
- access checks are permission-based and can run on both server and client
- notifications are shared content + per-user delivery/read state
If you need full implementation details, data contracts, and extension patterns, see ORGANIZATIONS.md.
- Organization: tenant/workspace record (
organizations) - Membership: user role in an org (
organization_memberships) - Invitation: pending org join request (
invitations) - Notification: shared event payload (
notifications) - Notification delivery: per-user state (
notification_deliveries)
- Permission catalog lives in
permissions.ts - Role definitions live in
roles.ts - Role selectors in settings are dynamic from
roles.ts(no hardcoded list) - Use permission checks for authorization rather than role-name string checks
- Client hooks:
hooks/use-permission.ts - Server checks:
lib/organization-access.ts - Route/API guards:
lib/permission-route-guards.ts
- Inviting users creates invitation records and can send email when configured
- Invitation-related in-app notifications are created regardless of email setup
- Notification actions support multiple behaviors (
route,url,open_settings,alert_dialog, invitation accept action) - The shell bell dropdown surfaces notifications in
components/shell/shell-sidebar.tsx
All major product-level customization is centralized in config.ts.
| Field | Used In |
|---|---|
name |
Metadata title template, branding in shell/footer/nav, default email sender name |
description |
Metadata description |
url |
Metadata base URL + email link origin |
auth |
Login mode + email-confirmation requirements |
logo.full |
Landing nav/footer branding |
logo.icon |
Auth layout + dashboard shell branding |
nav |
Landing navigation links |
footer.categories |
Footer link columns |
footer.socials |
Footer social icons/links |
dashboard.sidebar.items |
Left rail icons + MDX rail mapping |
dashboard.sidebar.utilities |
Bottom utility links in sidebar panel |
dashboard.settings |
Settings modal sections/items and account menu shortcuts |
dashboard.sidebar.items[].type supports:
default: normal navigation itemmdx: docs item tied to a local folder
For mdx items:
folderis required for content discoveryhrefis the URL base (/docsin this template)- folder path is resolved from project root (e.g.
/content/docs)
Supported footer.socials[].platform values are typed in types/index.ts:
twittergithubdiscordlinkedinreddittelegram
Docs rendering and tree generation live in lib/mdx-docs.ts.
Behavior:
- Walks configured folder recursively
- Ignores dotfiles/dotfolders
- Supports
.mdxand.md - Uses
index.mdx/index.mdas folder landing page - Builds left sidebar tree from file/folder structure
- Builds right-side TOC from
##and###headings only - Uses
titleanddescriptionfrontmatter when available
Route mapping examples (with default folder content/docs):
content/docs/index.mdx->/docscontent/docs/getting-started/index.mdx->/docs/getting-startedcontent/docs/getting-started/installation.mdx->/docs/getting-started/installation
Key route groups:
(auth)for login/register/reset flows(dashboard)/(home)for overview/lists/tables/etc.(dashboard)/(projects)for project pages(dashboard)/(pages)for reusable/error pages(dashboard)/(docs)for MDX docs section
Important implementation detail:
- The rail navigation (thin icon column) is config-driven
- Inner sidebar contents are route-group-specific components and currently hardcoded in each group layout
app/
(auth)/
(dashboard)/
api/auth/
components/
auth/
landing/
shell/
settings/
ui/
content/docs/
lib/
auth.ts
auth.config.ts
auth-tokens.ts
auth-emails.ts
mdx-docs.ts
mongodb.ts
config.ts
types/Some configured links/routes are intentionally starter placeholders and are not implemented yet:
- Landing links like
/pricing,/blog,/about,/careers, etc. - Sidebar utility link
/help - Many settings pages are generic placeholder content
Implement these routes before production launch or adjust links in config.ts.
- Add provider env vars.
- Add provider setup in
lib/auth.config.ts. - Ensure provider key is handled in login/register provider UI mapping if needed.
- Add route files under
app/(dashboard)/.... - Add rail item in
config.tsif needed. - Add path prefix to
protectedRoutesinlib/auth.config.ts. - Add group-specific sidebar items in the relevant layout component.
- Add an
mdxrail item inconfig.tswithhref+folder. - Create markdown/mdx files under that folder.
- Ensure file/folder names match desired URL slugs.
-
MONGODB_URI environment variable is not set- Add
MONGODB_URIto.env.local.
- Add
-
RESEND_API_KEY environment variable is not set- Required for email confirmation, OTP, and forgot-password email flows.
-
Confirmation/reset links point to localhost in production
- Set
NEXT_PUBLIC_APP_URL(andNEXTAUTH_URL) to your production origin.
- Set
-
Login works but new protected section is still public
- Add the section path to
protectedRoutesinlib/auth.config.ts.
- Add the section path to
-
Sidebar docs tree is empty
- Verify
dashboard.sidebar.itemscontains anmdxitem with correcthrefand validfolderpath.
- Verify
- Set all required production env vars
- Use strong
NEXTAUTH_SECRET - Configure OAuth callback URLs correctly
- Set correct
siteConfig.url/NEXT_PUBLIC_APP_URL - Ensure placeholder links are replaced or implemented
- Run
pnpm buildandpnpm lint - Verify auth flows for selected login mode
This template is licensed under the MIT License. See LICENSE for details.