DEV Community

Cover image for I built a production ready NEXT.JS Boilerplate so you dont have to!
Salman Shahriar
Salman Shahriar

Posted on • Edited on

I built a production ready NEXT.JS Boilerplate so you dont have to!

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 glue code.

I kept rebuilding the same foundation across SaaS projects, so I stopped and built it once: Next Elite.

It is a frontend-first Next.js 16 + React 19 starter with BetterAuth, permission-based RBAC, type-safe i18n, testing, and CI. MIT-licensed and open source. No database layer included. You bring your own API.

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

TL;DR: git clone, npm install, cp .env.example .env, npm run dev. You get auth, RBAC dashboards, 6-language i18n, SEO, forms, testing, and CI. Try the demo.


Table of Contents

  1. What's Included
  2. Lighthouse Scores
  3. Who It's For
  4. Getting Started
  5. How It Works
  6. Project Structure
  7. Caveats
  8. Production Checklist
  9. Contributing

What's Included

Most Next.js starters are either too bare (a theme toggle and a TODO for auth) or too opinionated (Prisma, a specific DB, and an ORM baked in). Next Elite sits in the middle: it handles the frontend foundation and leaves the data layer to you.

Stack

  • Next.js 16 + React 19 (App Router, Server Components)
  • TypeScript 6, Tailwind CSS v4, Zod 4
  • Node.js 22 + npm 11 in CI

Auth & access

  • BetterAuth (email/password + optional Google OAuth)
  • Permission-based RBAC with requireUser and requirePermission
  • Parallel routes (@admin, @user) for role-based dashboards

i18n & SEO

  • next-intl with 6 languages (English, বাংলা, العربية with RTL, Français, Español, 简体中文)
  • Cookie-based locale, compile-time checked translation keys
  • One site.config.json file for OG tags, JSON-LD, sitemap, robots, and manifest

Data & forms

  • TanStack Query + ofetch (apiFetch with Zod parsing)
  • React Hook Form + Zod 4 for auth forms

DX & ops

  • T3 Env for validated environment variables
  • Sentry + pino logging + optional Upstash rate limiting
  • Vitest, Playwright, Lefthook, Commitlint 21, Knip, ESLint 9, Prettier
  • GitHub Actions CI, Renovate, Vercel Analytics
  • Docker + Vercel deploy (output: 'standalone')
  • Demo mode for quick local previews

Lighthouse Scores

The production build scores 100 in Performance, Accessibility, Best Practices, and SEO.

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

Tested on the live demo at next-elite-boilerplate.vercel.app. Server components by default, client components only where needed.


Who It's For

Good fit:

  • SaaS apps with multiple user roles
  • Internationalized products (LTR and RTL)
  • Frontends that consume an existing API or BFF

Not a good fit:

  • Single-page landing sites
  • Apps that need a database layer in the same repo

Getting Started

Prerequisites: Node.js 22.12+, npm.

git clone https://github.com/salmanshahriar/Next-Elite.git
cd Next-Elite
npm install
cp .env.example .env
npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000.

Demo login (enabled by default with NEXT_PUBLIC_DEMO_MODE=true):

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

Brand your app: edit src/features/site/site.config.json.

Key env vars (full list in .env.example):

  • 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 plus Google OAuth credentials
  • SKIP_ENV_VALIDATION=true for CI/Docker builds without secrets

Deploy on Vercel:

Deploy with Vercel

Copy env vars from .env.example into your Vercel project.

Deploy with Docker (Node 22 Alpine):

docker build -t next-elite .
docker run --rm --env-file .env -p 3000:3000 next-elite
Enter fullscreen mode Exit fullscreen mode

Or docker compose up --build.

Scripts:

Command What it does
npm run dev Dev server (Turbopack)
npm run build Production build
npm run start Production server
npm run analyze Bundle analysis
npm run typecheck tsc --noEmit
npm run lint ESLint + Prettier check
npm run lint:fix Auto-fix lint and format
npm run knip Find unused files, exports, and dependencies
npm run check typecheck + lint + knip + tests
npm run test Vitest
npm run e2e Playwright (starts dev server automatically)
npm run e2e:ui Playwright UI mode
npm run e2e:webkit WebKit only

How It Works

Authentication

BetterAuth runs at /api/auth/* via src/app/api/auth/[...all]/route.ts.

  • Email/password works out of the box
  • Google OAuth: set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true
  • Server: getCurrentUser() in Server Components
  • Client: useAuth() in the auth provider
  • Admin role: comma-separated emails in AUTH_ADMIN_EMAILS

For multi-instance production, add a BetterAuth storage adapter (database or Redis). See the BetterAuth docs.

Demo mode lives in src/features/auth/demo/. Delete that folder or set NEXT_PUBLIC_DEMO_MODE=false before shipping.

RBAC

Permissions are checked on the server, not with scattered if (role === 'admin') checks.

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

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

export default AdminDashboardPage;
Enter fullscreen mode Exit fullscreen mode

Parallel routes pick the right dashboard:

src/app/(protected)/
  @admin/dashboard/    Admin dashboard
  @user/dashboard/     User dashboard
  layout.tsx           Picks slot based on permissions
Enter fullscreen mode Exit fullscreen mode

To add a role: update permissions.ts, map it in roles.ts, and optionally add a new @<role>/ slot in (protected)/layout.tsx.

Request flow

  1. Server Component renders the page
  2. requireUser() or requirePermission() checks the session
  3. HTML goes to the browser with translations from messages/
  4. Client fetches live data via TanStack Query and apiFetch

i18n

Locale is stored in a cookie (NEXT_LOCALE), not in the URL path.

To add a language:

  1. Add the locale to languages.supported and languages.locales in site.config.json
  2. Create messages/<locale>.json based on messages/en.json

SEO

src/features/site/site.config.json drives layout meta tags, sitemap, robots.txt, manifest, and locale config. Validated with Zod in src/features/site/config.ts.

{
  "appName": "Next Elite",
  "domain": "https://yourdomain.com",
  "title": "Next Elite - Production-Ready SaaS Boilerplate",
  "description": "Frontend-first Next.js 16 + React 19 boilerplate with i18n, RBAC and BetterAuth.",
  "languages": {
    "supported": ["en", "bn", "ar", "fr", "es", "zh"],
    "default": "en"
  },
  "organization": {
    "name": "Your Organization",
    "url": "https://yourdomain.com"
  },
  "images": {
    "og": "/Nextjs-Elite-OG-Image.webp",
    "cover": "/Nextjs-Elite-Cover.webp"
  },
  "manifest": "/manifest.webmanifest"
}
Enter fullscreen mode Exit fullscreen mode

API layer

apiFetch in src/libs/api-client.ts uses ofetch with Zod parsing. The users feature is an example pattern. You wire up /api/users or point NEXT_PUBLIC_APP_URL at your backend.

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

Forms

Auth forms use React Hook Form with Zod 4 resolvers. Schemas are in src/features/auth/schemas/.

Environment variables

Validated in src/libs/env.ts via T3 Env. BETTER_AUTH_URL is optional (derived from VERCEL_URL on Vercel). Missing BETTER_AUTH_SECRET in production logs a warning instead of crashing the build.

UI

shadcn/ui components in src/components/ui/. Custom theme provider in src/features/theme/ (light, dark, system). No next-themes dependency.

Testing & CI

  • Unit tests: Vitest + React Testing Library in tests/ and colocated *.test.ts(x) files
  • E2E: Playwright in e2e/ across Chromium, Firefox, and WebKit
  • CI: .github/workflows/check.yml runs typecheck, lint, knip, tests, and build
  • Health endpoint: GET /api/health returns { "status": "ok" }

proxy.ts is a pass-through middleware. Auth and locale run in Server Components and the next-intl request config.


Project Structure

Feature-sliced layout. Each feature owns its components, hooks, schemas, and server logic. Shared infra lives in libs/.

.
├── .github/workflows/        check.yml + playwright.yml
├── config/                   vitest config
├── e2e/                      Playwright specs
├── messages/                 translations (en, bn, ar, fr, es, zh)
├── tests/                    auth and i18n specs
├── src/
│   ├── app/                  App Router
│   │   ├── (auth)/           login, register, password reset
│   │   ├── (public)/         marketing pages
│   │   ├── (protected)/      @admin and @user dashboard slots
│   │   └── api/              BetterAuth + health
│   ├── components/ui/        shadcn primitives
│   ├── features/
│   │   ├── auth/             BetterAuth, RBAC, demo
│   │   ├── i18n/             next-intl config
│   │   ├── site/             site.config.json
│   │   └── users/            example API feature
│   └── libs/                 env, api-client, logger, utils
└── package.json
Enter fullscreen mode Exit fullscreen mode

Caveats

  1. No database. The users client expects your /api/users or an external API. No mock DB ships with the repo.
  2. Starter RBAC. Two roles (user, admin). Admin is an email allowlist, not a DB table. Extend in your backend for multi-tenant needs.
  3. Cookie-based locale. No /en/ or /fr/ URL prefixes. Simpler UX, but not path-prefixed locales for multilingual SEO.
  4. BetterAuth sessions. Default storage works locally. Use a storage adapter for multi-instance production.
  5. Rate limiting. getRateLimiter() exists in src/libs/rate-limit.ts but is not wired to routes by default.
  6. Test coverage. RBAC/i18n unit tests and light E2E. Not full app coverage.

Production Checklist

  1. Set BETTER_AUTH_SECRET (32+ characters)
  2. Add a BetterAuth storage adapter for multi-instance deploys
  3. Set NEXT_PUBLIC_DEMO_MODE=false or delete src/features/auth/demo/
  4. Implement /api/users or connect your real API
  5. Update site.config.json with your real domain and OG image
  6. Optionally enable Sentry and Upstash rate limiting

Contributing

  1. Fork and branch from main (feat/..., fix/...)
  2. Run npm run check locally
  3. Use Conventional Commits (enforced by Lefthook)
  4. Open a PR

Report a bug · Star on GitHub · Submit a PR


MIT licensed. If this saves you setup time, a star on GitHub helps others find it.

Live Demo · GitHub · Use Template · Deploy on Vercel

Top comments (22)

Collapse
 
jtorchia profile image
Juan Torchia

50 hours is an honest number — most boilerplate authors underestimate the invisible cost of the glue layer (auth callbacks, RBAC middleware, i18n routing edge cases). One thing I'd push on: how does the RBAC model handle permission inheritance vs. flat role assignment? In my experience that's the first thing that breaks when a real client asks for 'editor who can publish but only their own content'. The difference between a boilerplate that ships fast and one that survives the first feature request is usually that one decision.

Collapse
 
salmanshahriar profile image
Salman Shahriar

Great point!
and you’re exactly right that RBAC inheritance is where “simple boilerplate auth” usually collapses.

In this boilerplate, the model is intentionally flat role assignment + explicit permission checks, with ownership-aware rules layered in policy helpers (for cases like “publish own only”). That keeps the base predictable, but it does mean inheritance trees (e.g. admin > editor > author) are not first-class by default.

Collapse
 
anni profile image
Anietie Brownson

This is awesome
Good work

Collapse
 
salmanshahriar profile image
Salman Shahriar

Thanks man ‼️

Collapse
 
samihamahin profile image
Samiha Muntaha Mahin

Thats amazingg

Collapse
 
salmanshahriar profile image
Salman Shahriar

Thanks ‼️

Collapse
 
fahad_alikhan_5ec98c68a2 profile image
Fahad Ali Khan

Nice simple design. I'll surely use it in the future.

Collapse
 
salmanshahriar profile image
Salman Shahriar

❤️❤️

Collapse
 
sharafat_hossain_3dcc1f75 profile image
Sharafat Hossain

Mad respect man

Collapse
 
salmanshahriar profile image
Salman Shahriar

Thanks‼️

Collapse
 
leob profile image
leob

Nice! Bookmarked, and kept for "whenever I need it"

Collapse
 
salmanshahriar profile image
Salman Shahriar

Thanks, man! Feel free to share any suggestions anytime! ❤️

Collapse
 
sadi_nahin_5b099dbb050907 profile image
Sadi Nahin

Coolest shit, needed this!

Collapse
 
salmanshahriar profile image
Salman Shahriar

Thanks, man! Feel free to share any suggestions anytime! ❤️

Collapse
 
popy_chan_7f62c449a0c66c2 profile image
popy chan

damn! Good Work brother.

Collapse
 
salmanshahriar profile image
Salman Shahriar

thanks!

Collapse
 
joey_tribbiani_4771602dc5 profile image
Joey Tribbiani

Thanks for this blog.

Collapse
 
salmanshahriar profile image
Salman Shahriar • Edited

How you DOINNNN xD

Some comments may only be visible to logged-in visitors. Sign in to view all comments.