Documentation
urnext.app starter kit
Everything you need to clone, configure, and ship. This kit pairs Next.js 16, MongoDB, and Prisma with email/password & Google auth, multi-tenant routing, and white-label branding out of the box.
Overview
urnext.app is a production-ready SaaS starter kit. The public landing page lives at /; once a user signs in they get a dedicated tenant space at /{displayName} with their own dashboard and profile.
- Custom JWT auth in HTTP-only cookies (no third-party auth vendor lock-in)
- Approval-gated signup: applicants request access, admins approve/deny from a dashboard
- Password reset and optional Google OAuth (also subject to approval)
- Role-based access control (
USER/MODERATOR/ADMIN) - White-label branding & theme color driven by env vars or the database
- Accessible (WCAG AA) light/dark theming and a responsive sidebar shell
Tech stack
- Framework: Next.js 16 (App Router, Turbopack)
- Language: TypeScript, React 19
- Database: MongoDB via Prisma 6
- Auth: Custom JWT (
jose) +bcryptjs - UI: Tailwind CSS 4 + shadcn/ui (Radix)
- Email: Nodemailer (SMTP)
- State: Zustand + TanStack Query
- Deploy: Vercel-ready
Quick start
Get a local instance running in about five minutes:
# 1. Install dependencies
npm install
# 2. Create your own .env (copy the example) and fill in your values
cp .env.example .env
# 3. Once your environment is configured, prepare the database in one step
# (generates the Prisma client, pushes the schema, seeds branding)
npm run db:setup
# 4. Create your first admin (to review access requests)
ADMIN_EMAIL=you@example.com npm run seed:admin
# 5. Start the dev server
npm run dev # http://localhost:3000Generate a strong JWT_SECRET with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Environment variables
Create your own .env file in the project root — copy .env.example as a starting point and fill in your own values:
cp .env.example .envRequired for production: DATABASE_URL, JWT_SECRET, APP_URL. Every available variable — database, auth, email, Google OAuth, and white-label branding — is listed and documented inline in .env.example. Everything else is optional.
Project structure
app/
(home)/ # Public SPA landing
[workspace]/ # Authenticated tenant dashboard (URL = displayName)
auth/ # Login, signup, verify, reset, complete-profile
docs/ # This documentation page
api/auth/ # signup, login, verify, google, me, logout
api/user/ # profile, email, password, theme
lib/ # auth (JWT), email, google, site-config, rate-limit
components/ # auth forms, dashboard shell, shadcn/ui primitives
prisma/schema.prisma # MongoDB models (User, Account, Session, ...)
proxy.ts # Route gating (Next.js 16 middleware equivalent)Authentication & access requests
Registration is approval-gated. The signup page is a short application (name + email only) — it does not create a password. The flow:
- Applicant submits name + email at
/auth/signup→ aPENDINGaccount is created (no password). - An admin reviews it at
/adminand approves or denies. - On approval, the system generates a temporary password and emails it to the applicant.
- The applicant logs in and is forced to set a new password before reaching the app.
Approved users receive a JWT in a secure HTTP-only cookie (7-day expiry). Google OAuth (when configured) is subject to the same approval gate. Bootstrap your first admin with ADMIN_EMAIL=you@example.com npm run seed:admin.
Route protection is centralized in proxy.ts. Read the current user on the client with the useAuth() hook.
Multi-tenancy
Each user gets a unique displayName slug at signup. Their tenant URL is /{displayName} — backed by the dynamic app/[workspace] route. Unauthenticated visits to a protected route redirect to /auth/login; authenticated users without a displayName are sent to /auth/complete-profile.
White-label & theming
Branding (company name, logo, site title/description, primary color) resolves with the priority database > environment variable > default. Set env vars and run the seed script, or edit the site_config collection directly.
npm run seed:site-configThe primary theme color is injected at runtime from BRAND_PRIMARY / BRAND_PRIMARY_DARK and sanitized before being written to a <style> tag. For deeper restyling (neutrals, per-component vars) edit the CSS variables in app/globals.css.
Database
MongoDB through Prisma 6. There are no migration files — sync the schema with prisma db push and regenerate the client with prisma generate after any schema change.
npm run db:setup # first-time: generate client + push schema + seed branding
npx prisma generate # regenerate client after schema changes
npx prisma db push # sync schema to MongoDB
npx prisma studio # browse data in a GUIStay on Prisma 6.x — Prisma 7 dropped MongoDB support.
Deployment
The kit is Vercel-ready (vercel.json is included). Set the environment variables in your hosting dashboard, point DATABASE_URL at MongoDB Atlas, and deploy. The build command runs prisma generate && next build.
npm run build # prisma generate && next build
npm start # run the production serverFAQ
Where do I change the brand name? Set COMPANY_NAME / SITE_TITLE in .env (or the site_config collection), then re-seed.
How do I add an admin? Set a user's role to ADMIN in the database. Admin routes are gated in proxy.ts.
Is there a test suite? Not yet — wire up your preferred runner (Vitest / Playwright) as you build.
Full reference lives in README.md at the repo root.