DEV Community

Cover image for Next.js SaaS Boilerplate with BetterAuth, RBAC, i18n & Production-Ready Setup
Salman Shahriar
Salman Shahriar

Posted on

Next.js SaaS Boilerplate with BetterAuth, RBAC, i18n & Production-Ready Setup

Every time I started a new Next.js project, I lost the first week to setup.

Authentication. Internationalization. Role-based access. SEO meta tags. Environment validation. Error monitoring. Linting. Testing. CI pipelines.

By the time I had a working foundation, the excitement was gone, buried under config files and boilerplate glue code.

After doing this across multiple SaaS projects, I stopped and asked myself: What if I built the foundation once, properly, and never had to do it again?

So I did. Then I rebuilt it from scratch with a feature-sliced architecture and a clearer frontend-first scope.

The result is Nextjs-Elite-Boilerplate (v0.3.0): a production-ready, frontend-first Next.js 16 starter that's MIT-licensed and open source.

๐Ÿ”— Live Demo ยท ๐Ÿ“ฆ GitHub Repo ยท ๐Ÿš€ Use This Template ยท Deploy on Vercel

Next.js Elite โ€” production-ready SaaS boilerplate cover

Next.js Elite Boilerplate โ€” Open Graph preview


Table of Contents

  1. Why Another Boilerplate?
  2. What Makes This One Different
  3. Honest Caveats (So You Know What You're Getting)
  4. The Full Feature Breakdown
  5. Lighthouse? All 100s.
  6. Getting Started in 5 Minutes
  7. Production Checklist
  8. The Project Structure (For Humans)
  9. Everything You Get Out of the Box
  10. Who Should Use This (and Who Shouldn't)
  11. Contributing
  12. Final Thoughts

Why Another Boilerplate?

I know what you're thinking. The world doesn't need another Next.js starter.

Honestly? Most of the time, you'd be right. Most starters fall into two camps:

  1. Too bare. A create-next-app with a theme toggle and a "TODO: add auth here" comment.
  2. Too opinionated. Ships with a Prisma schema, a specific database, and an ORM you didn't ask for, tightly coupling you to decisions you haven't made yet.

I wanted something in between: a frontend-first foundation that handles auth, roles, i18n, SEO, forms, data fetching, error monitoring, and developer tooling, but doesn't force a database on you. You bring your own API (REST, GraphQL, BFF, whatever), and this boilerplate handles everything else.

That's what Next.js Elite is. And it's free, MIT-licensed, and open source.


What Makes This One Different

Here's what you get with a single git clone:

  • Next.js 16 + React 19 with App Router and Server Components
  • BetterAuth with email/password and optional Google OAuth
  • Permission-based RBAC using parallel routes (@admin, @user)
  • Type-safe i18n via next-intl: 6 languages, RTL, cookie-based locale, compile-time checked keys
  • T3 Env for Zod-validated environment variables (server + client split)
  • TanStack Query + ofetch (apiFetch) for API data fetching with Zod response parsing
  • React Hook Form + Zod for forms with shared validation schemas
  • Sentry for error monitoring, pino for server logging
  • Optional Upstash helpers for rate limiting (when env vars are set)
  • Vitest + Playwright for unit and E2E testing
  • Lefthook + Commitlint + Knip for automated code quality
  • One JSON file drives SEO, sitemap, robots.txt, and manifest config
  • Demo mode for instant previews on the login page
  • Docker + Vercel deployment ready (output: 'standalone')

And the Lighthouse scores? Well...


Honest Caveats (So You Know What You're Getting)

The homepage hero cards describe what ships today. These six caveats match those cards and what the repo actually does โ€” no overselling.

  1. Modern stack, lean setup โ€” API-driven by design: no ORM or database layer. The example users client expects your /api/users or an external API; no mock DB is included.

  2. BetterAuth โ€” Auth works out of the box for local and demo use. For multi-instance production, configure a Better Auth storage adapter so sessions persist across instances (BetterAuth defaults are not shared across nodes).

  3. Parallel routing โ€” @user / @admin slots share /dashboard, but permissions are starter-grade RBAC (two roles, email allowlist for admin). Extend roles in your backend when you need multi-tenant or org-level access.

  4. Type-safe i18n โ€” Locale lives in a cookie (NEXT_LOCALE; no /en or /fr URL prefixes): simpler UX, not path-prefixed locales for multilingual SEO.

  5. SEO + PWA, server-first โ€” Metadata, sitemap, robots, and JSON-LD come from site.config.json. Replace placeholder org/domain values before you ship.

  6. Developer experience โ€” CI runs typecheck โ†’ lint โ†’ knip โ†’ test โ†’ build. Test coverage is starter-level (RBAC/i18n unit tests + light E2E). Optional getRateLimiter() exists in src/libs/rate-limit.ts but is not wired to auth or API routes by default.


Lighthouse? All 100s.

This isn't just "good enough for a starter." The boilerplate scores 100 across all four Lighthouse categories: Performance, Accessibility, Best Practices, and SEO, right out of the box.

Lighthouse report showing perfect 100 scores in Performance, Accessibility, Best Practices, and SEO

No tricks. No deferred audits. That's the production build, tested against the live demo at nextjs-elite-boilerplate.vercel.app.

Most boilerplates treat performance as an afterthought. Here, it's baked in from day one: server components by default, client components only where needed, semantic HTML, proper heading hierarchy, and optimized asset loading.


The Full Feature Breakdown

Let's walk through every major system. I'll cover what it does, why I chose it, and how it works in this codebase.

Authentication: BetterAuth

Auth is built on BetterAuth, exposed at /api/auth/* via src/app/api/auth/[...all]/route.ts.

Here's what you get:

  • Email/password sign-up and login โ€” works immediately
  • Google OAuth โ€” set GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET and NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true
  • Server-side session reads โ€” getCurrentUser() in Server Components; client uses authClient.useSession() via the auth provider
  • Admin role mapping โ€” comma-separated emails in AUTH_ADMIN_EMAILS / NEXT_PUBLIC_AUTH_ADMIN_EMAILS

Sessions use BetterAuth's default storage. For production on multiple instances, plug in a database or Redis adapter โ€” see the BetterAuth docs.

Demo mode lives in src/features/auth/demo/. With NEXT_PUBLIC_DEMO_MODE=true, the login page shows click-to-fill credentials and auto-registers seed accounts on first sign-in:

Role Email Password
User user@test.com 12345678
Admin admin@test.com 12345678

Going to production? Set the flag to false, or delete the demo/ folder. Nothing else imports it except the login flow.

RBAC: Permissions, Not Just Roles

Most boilerplates give you if (role === 'admin') checks scattered across your code. That doesn't scale.

Next.js Elite uses permission-based RBAC. Each role maps to permissions; pages check permissions, not raw role strings:

import { requirePermission } from '@/features/auth/rbac/require';

const AdminDashboardPage = async () => {
  const user = await requirePermission('dashboard.view:admin');
  return <h1>Welcome back, {user.email}</h1>;
};
Enter fullscreen mode Exit fullscreen mode

Invalid sessions redirect to /login. Insufficient permissions redirect to /unauthorized.

The routing layer uses Next.js parallel routes:

src/app/(protected)/
  @admin/dashboard/    โ†’ Admin sees this
  @user/dashboard/     โ†’ Regular users see this
  layout.tsx           โ†’ Picks the right slot based on permissions
Enter fullscreen mode Exit fullscreen mode

Today there are two roles (user, admin) and two dashboard permissions. Admin is granted via email allowlist, not a database role table โ€” extend rbac/roles.ts and your backend when you outgrow that.

Internationalization: next-intl with Type Safety

The boilerplate uses next-intl with:

  • Cookie-based locale (NEXT_LOCALE) โ€” no /en/ or /fr/ URL prefixes
  • 6 languages out of the box: English, เฆฌเฆพเฆ‚เฆฒเฆพ, ุงู„ุนุฑุจูŠุฉ (full RTL), Franรงais, Espaรฑol, ็ฎ€ไฝ“ไธญๆ–‡
  • Compile-time type checking โ€” t("navigation.home") autocompletes; typos fail the build

Translation files live in messages/. Add a language in site.config.json, create messages/<locale>.json, and the runtime picks it up.

SEO: One Config File

SEO is driven by src/features/site/site.config.json, validated with Zod in src/features/site/config.ts.

That one file drives:

  • Open Graph and Twitter Card meta tags
  • JSON-LD structured data (Organization, WebSite, SoftwareApplication)
  • Dynamic sitemap and robots.txt
  • PWA web app manifest
  • Canonical URLs and theme colors
{
  "appName": "My SaaS",
  "domain": "https://mysaas.com",
  "title": "My SaaS โ€” Do X Better",
  "description": "We help Y achieve Z.",
  "organization": {
    "name": "My Company",
    "url": "https://mysaas.com"
  },
  "images": { "og": "/og-image.webp" }
}
Enter fullscreen mode Exit fullscreen mode

Data Fetching: ofetch + TanStack Query

The API layer uses ofetch via apiFetch in src/libs/api-client.ts (defaults to same-origin /api) and TanStack Query on the client.

The users feature is a copy-paste pattern โ€” you implement /api/users or point NEXT_PUBLIC_APP_URL at your backend:

import { useUsers } from '@/features/users/hooks/use-users';

const { data, isLoading } = useUsers();
Enter fullscreen mode Exit fullscreen mode
// src/features/users/api.ts
export async function getUsers(): Promise<User[]> {
  return apiFetch('/users', { schema: userListSchema });
}
Enter fullscreen mode Exit fullscreen mode

Forms: React Hook Form + Zod

Every auth form uses React Hook Form with Zod resolvers. Schemas live in src/features/auth/schemas/:

import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { loginSchema, type LoginInput } from '@/features/auth/schemas/login';

const form = useForm<LoginInput>({
  resolver: zodResolver(loginSchema),
  defaultValues: { email: '', password: '' },
});
Enter fullscreen mode Exit fullscreen mode

Environment Variables: T3 Env

@t3-oss/env-nextjs + Zod validates server and client env vars at build time. SKIP_ENV_VALIDATION=true is supported for CI and Docker builds. A missing BETTER_AUTH_SECRET in production logs a warning at runtime (placeholder only for local dev).

UI: shadcn/ui + Tailwind CSS v4

shadcn/ui (Radix + CVA + Tailwind v4). Components live in src/components/ui/ โ€” copy and own them.

Dark mode uses a lightweight custom theme provider in src/features/theme/ (system / light / dark) with a toggle in the header โ€” no next-themes dependency.

Observability: Sentry + pino + Rate Limiting

  • Sentry โ€” src/instrumentation.ts (server) and src/instrumentation-client.ts (client). Set SENTRY_DSN / NEXT_PUBLIC_SENTRY_DSN.
  • pino โ€” structured server logging in src/libs/logger.ts.
  • @upstash/ratelimit โ€” optional getRateLimiter() / isRateLimited() in src/libs/rate-limit.ts when UPSTASH_REDIS_* is set. Wire it into your route handlers where you need protection.

Testing: Vitest + Playwright

  • Unit tests: Vitest + React Testing Library. Specs in tests/ (auth, i18n) and colocated *.test.tsx under src/components/ui/ and src/libs/.
  • E2E: Playwright in e2e/ โ€” home, health check, login page smoke tests. Run npm run e2e (config: e2e/playwright.config.ts).
  • CI: .github/workflows/check.yml (typecheck โ†’ lint โ†’ knip โ†’ test โ†’ build) and playwright.yml for E2E.

Developer Experience: The Full Pipeline

  • Lefthook โ€” pre-commit lint/format, commit-msg Commitlint
  • Knip โ€” unused files, exports, and dependencies
  • Renovate โ€” automated dependency updates (.github/renovate.json)
  • ESLint 9 flat config + Prettier (Tailwind class sorting)
  • Vercel Analytics โ€” included in the root layout

proxy.ts is a pass-through middleware placeholder โ€” auth and locale are handled in Server Components and next-intl request config, not at the edge.


Getting Started in 5 Minutes

Prerequisites: Node.js 20.9+, npm / pnpm / yarn / bun.

1. Clone and install

git clone https://github.com/salmanshahriar/Nextjs-Elite-Boilerplate.git
cd Nextjs-Elite-Boilerplate
npm install
Enter fullscreen mode Exit fullscreen mode

2. Set up environment

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Key variables (see .env.example for the full list):

  • BETTER_AUTH_SECRET โ€” 32+ chars in production (openssl rand -base64 32)
  • BETTER_AUTH_URL โ€” optional; auto-derived on Vercel
  • NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true + Google credentials for OAuth
  • NEXT_PUBLIC_DEMO_MODE=true for instant test accounts
  • SKIP_ENV_VALIDATION=true for CI/Docker when secrets aren't available yet

3. Brand it

Edit src/features/site/site.config.json โ€” app name, domain, SEO, social links, locales.

4. Add languages (optional)

Add the locale to site.config.json, create messages/<locale>.json.

5. Run

npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000.

All available scripts

Command What it does
npm run dev Dev server
npm run build Production build
npm run start Production server
npm run analyze Bundle analysis (ANALYZE=true)
npm run typecheck tsc --noEmit
npm run lint ESLint + Prettier check
npm run lint:fix Auto-fix lint + format
npm run knip Dead code / dependency detection
npm run check typecheck + lint + knip + test (CI gate)
npm run test Vitest run (config/vitest.config.ts)
npm run test:watch Vitest watch
npm run e2e Playwright E2E
npm run e2e:ui Playwright UI mode
npm run e2e:webkit WebKit-only E2E

Deploy

Vercel (one click):

Deploy with Vercel

Docker:

docker build -t nextjs-elite .
docker run --rm --env-file .env -p 3000:3000 nextjs-elite
# or: docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Production Checklist

Before you ship:

  1. Set a strong BETTER_AUTH_SECRET (32+ characters).
  2. Configure a Better Auth storage adapter for multi-instance deploys.
  3. Set NEXT_PUBLIC_DEMO_MODE=false or remove src/features/auth/demo/.
  4. Implement /api/users (or your real API) for features that call apiFetch.
  5. Update site.config.json โ€” real domain, org, and OG image (not placeholders).
  6. Optionally enable Sentry and Upstash rate limiting on sensitive routes.

The Project Structure (For Humans)

Feature-sliced architecture: each feature owns its components, hooks, schemas, and server logic. Cross-cutting infra lives in libs/.

.
โ”œโ”€โ”€ .github/workflows/        check.yml + playwright.yml
โ”œโ”€โ”€ config/                   vitest.config.ts, vitest.setup.ts
โ”œโ”€โ”€ e2e/                      Playwright specs + playwright.config.ts
โ”œโ”€โ”€ messages/                 Translation files (en, bn, ar, fr, es, zh)
โ”œโ”€โ”€ tests/                    Vitest specs (auth, i18n)
โ”œโ”€โ”€ proxy.ts                  Middleware (pass-through)
โ”œโ”€โ”€ lefthook.yml              Git hooks
โ”œโ”€โ”€ knip.json                 Dead-code config
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ app/                  App Router
โ”‚   โ”‚   โ”œโ”€โ”€ (auth)/           Login, register, password reset
โ”‚   โ”‚   โ”œโ”€โ”€ (public)/         Marketing pages
โ”‚   โ”‚   โ”œโ”€โ”€ (protected)/      Auth-gated area
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ @admin/       Admin dashboard slot
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ @user/        User dashboard slot
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ layout.tsx    Permission-based slot picker
โ”‚   โ”‚   โ”œโ”€โ”€ api/              BetterAuth + health
โ”‚   โ”‚   โ”œโ”€โ”€ layout.tsx        Root layout + SEO
โ”‚   โ”‚   โ””โ”€โ”€ providers.tsx     Theme + Auth + Query
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”œโ”€โ”€ shared/           Hero, logo, home sections
โ”‚   โ”‚   โ””โ”€โ”€ ui/               shadcn/ui primitives
โ”‚   โ”œโ”€โ”€ features/
โ”‚   โ”‚   โ”œโ”€โ”€ auth/             BetterAuth, RBAC, demo mode
โ”‚   โ”‚   โ”œโ”€โ”€ i18n/             next-intl config
โ”‚   โ”‚   โ”œโ”€โ”€ navigation/       Header + Sidebar
โ”‚   โ”‚   โ”œโ”€โ”€ site/             site.config.json + Zod parser
โ”‚   โ”‚   โ”œโ”€โ”€ theme/            Custom theme provider
โ”‚   โ”‚   โ””โ”€โ”€ users/            Example API + hooks
โ”‚   โ””โ”€โ”€ libs/                 env, api-client, logger, rate-limit, utils
โ””โ”€โ”€ package.json              scripts; Prettier + Commitlint config
Enter fullscreen mode Exit fullscreen mode

Key conventions:

  • features/<name>/ โ€” vertical slices
  • libs/ โ€” cross-cutting infra, no business logic
  • components/ui/ โ€” shadcn primitives; extend, don't fight the CLI
  • schemas/ โ€” shared Zod schemas (e.g. API responses)

Everything You Get Out of the Box

โœ… Next.js 16 + React 19 (App Router, Server Components)
โœ… TypeScript 5.9 (strict)
โœ… Tailwind CSS v4
โœ… BetterAuth (email/password + optional Google OAuth)
โœ… Permission-based RBAC with parallel routes
โœ… next-intl (6 languages, RTL, type-safe, cookie-based)
โœ… T3 Env (Zod-validated server + client env vars)
โœ… TanStack Query + ofetch (apiFetch + Zod parsing)
โœ… React Hook Form + Zod (auth forms + shared schemas)
โœ… SEO suite (OG, Twitter Cards, JSON-LD, sitemap, robots, manifest)
โœ… Custom theme provider (light / dark / system)
โœ… shadcn/ui (Radix + CVA + Tailwind)
โœ… Sentry (server + client instrumentation)
โœ… pino (structured server logging)
โœ… Optional Upstash rate limiting helpers
โœ… Vitest + React Testing Library
โœ… Playwright E2E
โœ… Lefthook + Commitlint
โœ… Knip (dead-code hygiene)
โœ… ESLint + Prettier
โœ… GitHub Actions CI + Playwright workflow
โœ… Docker + Docker Compose (standalone output)
โœ… Demo mode (isolated src/features/auth/demo/)
โœ… Health check (GET /api/health)
โœ… Renovate + Vercel Analytics
โœ… Vercel one-click deploy


Who Should Use This (and Who Shouldn't)

This is a good fit if you're building:

  • A SaaS product with multiple user roles
  • An internationalized app (especially with RTL requirements)
  • A frontend that consumes an existing API or BFF
  • A project where you want auth, RBAC, i18n, and DX tooling on day one without inheriting a database

Probably not the right choice for:

  • A single-page landing site (more than you need)
  • An app that needs a tightly-coupled database layer in the same repo (this is intentionally API-only)
  • Enterprise RBAC with orgs, teams, and dynamic roles in the DB (start here, extend in your backend)

Contributing

The project is fully open source and contributions are welcome:

  1. Fork and branch from main (feat/..., fix/...)
  2. Run npm run check โ€” it must pass
  3. Use Conventional Commits (Lefthook enforces this)
  4. Open a PR with a clear description

๐Ÿ› Report a bug ยท โญ Star on GitHub ยท ๐Ÿค Submit a PR


Final Thoughts

I built the first version because I was tired of repeating myself. Then I rebuilt it with feature-sliced folders, BetterAuth, type-safe env and i18n, parallel-route dashboards, and a DX pipeline (Lefthook, Knip, Vitest, Playwright) that matches what I actually run before every push.

The goal was never the biggest starter kit. It was the one I want on day one of a SaaS frontend โ€” and still trust on day one hundred, with a backend I chose myself.

If it saves you a week of glue code, it was worth open-sourcing.

What's your biggest pain point when starting a new Next.js project? Drop a comment โ€” I'm always looking for improvements that stay frontend-first.

Happy building. ๐Ÿš€


๐Ÿ”— Live Demo ยท ๐Ÿ“ฆ GitHub ยท ๐Ÿš€ Use Template ยท Deploy on Vercel

Top comments (0)