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 better architecture.
The result is Nextjs-Elite-Boilerplate: a production-ready, frontend-first Next.js 16 starter kit that's completely open source.
🔗 Live Demo · 📦 GitHub Repo · 🚀 Use This Template · Deploy on Vercel
Table of Contents
- Why Another Boilerplate?
- What Makes This One Different
- The Full Feature Breakdown
- Lighthouse? All 100s.
- Getting Started in 5 Minutes
- The Project Structure (For Humans)
- Everything You Get Out of the Box
- Who Should Use This (and Who Shouldn't)
- Contributing
- 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:
-
Too bare. A
create-next-appwith a theme toggle and a "TODO: add auth here" comment. - 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, Google OAuth, and Upstash Redis sessions
-
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 for API data fetching with caching
- React Hook Form + Zod for forms with shared validation schemas
- Sentry for error monitoring, pino for server logging
- Storybook 10 for isolated component development
- Vitest + Playwright for unit and E2E testing
- Lefthook + Commitlint + Knip for automated code quality
- One JSON file drives all SEO, sitemap, robots.txt, and manifest config
- Docker + Vercel deployment ready
And the Lighthouse scores? Well...
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.
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, zero layout shift, 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.
Authentication: BetterAuth + Upstash Redis
I originally built this with NextAuth. It worked, but I hit limitations around serverless session persistence and flexibility. So I migrated to BetterAuth.
Here's what the auth system gives you:
- Email/password sign-up and login: works immediately
-
Google OAuth: flip one env var to enable:
NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true - Serverless sessions: uses Upstash Redis in production, falls back to in-memory for local dev
-
Admin role mapping: list admin emails in
AUTH_ADMIN_EMAILSand they get elevated permissions automatically
There's also a demo mode: a self-contained module at src/features/auth/demo/ that adds click-to-fill credentials and auto-registers seed accounts. Set NEXT_PUBLIC_DEMO_MODE=true and you get instant test users:
| Role | Password | |
|---|---|---|
| User | user@test.com |
12345678 |
| Admin | admin@test.com |
12345678 |
Going to production? Set the flag to false, or just delete the demo/ folder. Nothing else in the codebase depends on it.
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 a set of permissions, and your pages check for specific permissions, not raw role strings:
// This is all you need in a Server Component
import { requirePermission } from '@/features/auth/rbac/require';
const AdminDashboardPage = async () => {
const user = await requirePermission('dashboard.view:admin');
return <h1>Welcome back, {user.email}</h1>;
};
Invalid sessions redirect to /login. Insufficient permissions redirect to /unauthorized. No conditionals in your page components.
The routing layer uses Next.js 16 parallel routes to keep things clean:
src/app/(protected)/
@admin/dashboard/ → Admin sees this
@user/dashboard/ → Regular users see this
layout.tsx → Picks the right slot based on permissions
Want to add a moderator role? Three steps:
- Add it to the
UserRoleunion inrbac/permissions.ts - Map its permissions in
rbac/roles.ts - Optionally create a
@moderator/parallel route
That's it. No refactoring.
Internationalization: next-intl with Type Safety
I used to roll custom i18n. It worked until it didn't: missing keys in production, no RTL support, locale scattered across URL prefixes.
The boilerplate uses next-intl with:
-
Cookie-based locale: no
/en/or/fr/cluttering your URLs - 6 languages out of the box: English, বাংলা, العربية (with full RTL), Français, Español, 简体中文
-
Compile-time type checking:
t("navigation.home")autocompletes, and typos break the build, not production
Translation files live in messages/. Adding a new language is two steps: add it to site.config.json, create messages/<locale>.json. The runtime handles the rest.
SEO: One Config File
SEO in most projects means hunting down metadata across five different files. Here, it's a single source of truth: src/features/site/site.config.json.
That one file drives:
- Open Graph and Twitter Card meta tags
- JSON-LD structured data (
Organization+WebSiteschemas) - Language alternates for multilingual SEO
- Dynamic sitemap generation
- robots.txt
- PWA web app manifest
- Canonical URLs
The config is validated by a Zod schema at build time. Typo in a field name? The build fails with a clear error, not a silent SEO regression you discover months later.
{
"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" }
}
Data Fetching: ofetch + TanStack Query
The API layer is built on ofetch (typed HTTP) and TanStack Query (caching, retries, server-state). The boilerplate includes a fully worked users feature you can copy as a starting point:
import { useQuery } from '@tanstack/react-query';
import { getUsers } from '@/features/users/api';
const { data, isLoading } = useQuery({
queryKey: ['users', { page: 1 }],
queryFn: () => getUsers({ page: 1 }),
});
Forms: React Hook Form + Zod
Every form uses React Hook Form with Zod resolvers. Validation schemas are shared between client and server, so you define validation once and use it everywhere:
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: '' },
});
Environment Variables: T3 Env
No more .env typos silently breaking production. @t3-oss/env-nextjs + Zod validates every environment variable at build time with a clean server/client split. Missing a required secret? The build tells you immediately.
UI: shadcn/ui + Tailwind CSS v4
The component layer uses shadcn/ui (Radix + CVA + Tailwind CSS v4). Components are copy-and-own, no hidden dependency tree. Dark mode is handled by next-themes with system detection and a manual toggle.
Observability: Sentry + pino + Rate Limiting
-
Sentry is wired into both server (
instrumentation.ts) and client (instrumentation-client.ts). Drop in your DSN and you have production error tracking. - pino handles structured server logging with pretty output in dev.
- @upstash/ratelimit helpers are ready for protecting your route handlers.
Testing: Vitest + Playwright
-
Unit tests: Vitest + React Testing Library. Specs live in
tests/. -
E2E tests: Playwright in
e2e/. Runnpm run e2eand the dev server starts automatically. -
Coverage:
npm run test:coveragewith V8 coverage reports.
Developer Experience: The Full Pipeline
Most boilerplates stop at "ESLint + Prettier." This one goes further:
- Lefthook: auto-lint and format on pre-commit
- Commitlint: enforces Conventional Commits
- Knip: flags unused files, exports, and dependencies
-
Storybook 10: isolated component dev on
:6006 - Renovate: automates dependency updates
- GitHub Actions CI: typecheck → lint → knip → test → build on every push
Getting Started in 5 Minutes
Prerequisites: Node.js 20.9+, and your preferred package manager (npm/pnpm/yarn/bun).
1. Clone and install
git clone https://github.com/salmanshahriar/Nextjs-Elite-Boilerplate.git
cd Nextjs-Elite-Boilerplate
npm install
2. Set up environment
cp .env.example .env
The .env.example file documents every variable. Key ones:
-
BETTER_AUTH_SECRET: required in production (32+ chars). Generate withopenssl 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=truefor instant test accounts -
SKIP_ENV_VALIDATION=truefor CI/Docker builds where secrets aren't available yet
3. Brand it
Edit src/features/site/site.config.json: app name, domain, SEO, social links, organization details. One file, Zod-validated at build time.
4. Add languages (optional)
Add the locale to site.config.json, create messages/<locale>.json, done. The next-intl runtime picks it up automatically.
5. Run
npm run dev
Open http://localhost:3000. You're live.
All available 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 (@next/bundle-analyzer) |
npm run typecheck |
tsc --noEmit |
npm run lint |
ESLint + Prettier check |
npm run lint:fix |
Auto-fix lint + format |
npm run format |
Prettier check |
npm run format:fix |
Prettier write |
npm run knip |
Dead code/dependency detection |
npm run check |
Full CI gate (typecheck + lint + knip + test) |
npm run test |
Vitest run |
npm run test:watch |
Vitest watch mode |
npm run test:coverage |
Vitest with V8 coverage |
npm run e2e |
Playwright E2E |
npm run e2e:ui |
Playwright UI mode |
npm run e2e:webkit |
WebKit-only E2E |
npm run storybook |
Storybook on :6006
|
npm run storybook:build |
Static Storybook export |
Deploy
Vercel (one click):
Docker:
docker build -t nextjs-elite .
docker run --rm --env-file .env -p 3000:3000 nextjs-elite
# or: docker compose up --build
The Project Structure (For Humans)
The codebase uses a feature-sliced architecture. Each feature owns its components, hooks, schemas, and server logic. Cross-cutting infrastructure lives separately in libs/.
.
├── .github/workflows/ CI pipelines
├── e2e/ Playwright E2E specs
├── messages/ Translation files (en, bn, ar, fr, es, zh)
├── src/
│ ├── app/ Next.js App Router
│ │ ├── (auth)/ Login & registration
│ │ ├── (public)/ Marketing pages
│ │ ├── (protected)/ Auth-gated area
│ │ │ ├── @admin/ Admin dashboard slot
│ │ │ ├── @user/ User dashboard slot
│ │ │ └── layout.tsx Permission-based slot picker
│ │ ├── api/ Route handlers (auth, health)
│ │ ├── layout.tsx Root layout + SEO
│ │ ├── providers.tsx Theme + Auth + Query providers
│ │ ├── manifest.ts PWA manifest
│ │ ├── robots.ts robots.txt
│ │ └── sitemap.ts Dynamic sitemap
│ ├── components/
│ │ ├── shared/ App-level UI (logo, hero)
│ │ ├── icons/ Icon components
│ │ └── ui/ shadcn/ui primitives
│ ├── features/
│ │ ├── auth/ BetterAuth, RBAC, demo mode
│ │ ├── i18n/ next-intl config
│ │ ├── navigation/ Header + Sidebar
│ │ ├── site/ Site config + utilities
│ │ ├── theme/ Theme provider + toggle
│ │ └── users/ Example feature (copy this!)
│ ├── hooks/ Cross-feature hooks
│ ├── libs/ Infra: env, logger, API client, rate-limit
│ ├── schemas/ Shared Zod schemas
│ ├── instrumentation.ts Sentry (server)
│ └── instrumentation-client.ts Sentry (client)
├── tests/ Vitest specs
├── proxy.ts Middleware
├── lefthook.yml Git hooks
├── commitlint.config.js Conventional Commits
├── knip.json Dead-code config
├── playwright.config.ts
└── vitest.config.ts
Key conventions:
-
features/<name>/: vertical slices. Everything a feature needs lives here. -
libs/: cross-cutting infra (env, logger, API client). No business logic. -
components/ui/: shadcn/ui primitives. Copy and extend, don't edit in place. -
schemas/: shared Zod schemas used across multiple features.
Everything You Get Out of the Box
✅ Next.js 16 + React 19 (App Router, Server Components)
✅ TypeScript 5.9 (strict mode)
✅ Tailwind CSS v4
✅ BetterAuth (email/password + Google OAuth + Redis sessions)
✅ 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 (API layer with caching)
✅ React Hook Form + Zod (forms with shared validation)
✅ SEO suite (OG, Twitter Cards, JSON-LD, sitemap, robots, manifest)
✅ next-themes (dark mode with system detection)
✅ shadcn/ui (Radix + CVA + Tailwind)
✅ Sentry (server + client error monitoring)
✅ pino (structured server logging)
✅ Upstash rate limiting
✅ Storybook 10
✅ Vitest + React Testing Library
✅ Playwright E2E
✅ Lefthook + Commitlint (git hooks + conventional commits)
✅ Knip (dead-code hygiene)
✅ ESLint + Prettier (Next.js, TS, Tailwind, a11y)
✅ GitHub Actions CI
✅ Docker + Docker Compose
✅ Demo mode (self-contained seed accounts)
✅ Health check (GET /api/health)
✅ Renovate (automated dependency updates)
✅ 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
- An enterprise app that needs auth, RBAC, and observability from day one
Probably not the right choice for:
- A single-page landing site (this is more than you need)
- An app that requires a tightly-coupled database layer (this is intentionally API-only, bring your own backend)
Contributing
The project is fully open source and contributions are welcome:
- Fork and branch from
main(feat/...,fix/...) - Run
npm run check: it must pass - Use Conventional Commits (Lefthook enforces this)
- 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 tore it apart and rebuilt it with a feature-sliced architecture, migrated to BetterAuth, added type-safe everything (env vars, i18n keys, form schemas), and wired up a complete DX pipeline with Sentry, Storybook, Lefthook, and Knip.
The goal was never to build the biggest starter kit. It was to build the one I actually want to use on day one of every project, and still be glad I chose on day 100.
If it saves you a few hours of setup time, it was worth it.
What's your biggest pain point when starting a new Next.js project? Drop a comment. I'm always looking for ways to make this better.
Happy building. 🚀
🔗 Live Demo · 📦 GitHub · 🚀 Use Template · Deploy on Vercel


Top comments (22)
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.
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.
This is awesome
Good work
Thanks man ‼️
Thats amazingg
Thanks ‼️
Nice simple design. I'll surely use it in the future.
❤️❤️
Mad respect man
Thanks‼️
Nice! Bookmarked, and kept for "whenever I need it"
Thanks, man! Feel free to share any suggestions anytime! ❤️
Coolest shit, needed this!
Thanks, man! Feel free to share any suggestions anytime! ❤️
damn! Good Work brother.
thanks!
Thanks for this blog.
How you DOINNNN xD
Some comments may only be visible to logged-in visitors. Sign in to view all comments.