<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Salman Shahriar</title>
    <description>The latest articles on Forem by Salman Shahriar (@salman_shahriar).</description>
    <link>https://forem.com/salman_shahriar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3805011%2F79cfdcbb-7634-4771-a09a-d456604c4d11.jpeg</url>
      <title>Forem: Salman Shahriar</title>
      <link>https://forem.com/salman_shahriar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/salman_shahriar"/>
    <language>en</language>
    <item>
      <title>I Spent 50 Hours Building a Next.js Boilerplate So You Can Ship in 30 Minutes</title>
      <dc:creator>Salman Shahriar</dc:creator>
      <pubDate>Wed, 04 Mar 2026 03:42:44 +0000</pubDate>
      <link>https://forem.com/salman_shahriar/i-spent-50-hours-building-a-nextjs-boilerplate-so-you-can-ship-in-30-minutes-1pl3</link>
      <guid>https://forem.com/salman_shahriar/i-spent-50-hours-building-a-nextjs-boilerplate-so-you-can-ship-in-30-minutes-1pl3</guid>
      <description>&lt;h2&gt;
  
  
  The Problem That Kept Me Up at Night
&lt;/h2&gt;

&lt;p&gt;You know that feeling when you start a new Next.js project and spend the first week just setting things up? Authentication, internationalization, role management, SEO configuration... By the time you're done with the boilerplate, you've lost all that initial excitement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get in one line:&lt;/strong&gt; Type-safe i18n with RTL → NextAuth + Google OAuth → RBAC with parallel routes → SEO (sitemap, robots, manifest) → Dark mode → ESLint + Prettier → Vitest + Playwright → shadcn/ui → One config file. Production-ready.&lt;/p&gt;

&lt;p&gt;I've been there. Too many times.&lt;/p&gt;

&lt;p&gt;After launching my third SaaS project this year, I realized I was copy-pasting the same setup code over and over. So I decided to do something about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Meet my production-ready Next.js boilerplate: not just another "hello world" starter template, but a &lt;strong&gt;fully-featured foundation&lt;/strong&gt; that handles all the boring stuff so you can focus on building your actual product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important Links:
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://nextjs-boilerplate-production-ready.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/strong&gt; | 📦 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt; | 🚀 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready/generate" rel="noopener noreferrer"&gt;Use this template&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https://github.com/salmanshahriar/nextjs-boilerplate-production-ready" rel="noopener noreferrer"&gt;Deploy on Vercel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljg9676g7u95e8j17jki.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljg9676g7u95e8j17jki.webp" alt="Boilerplate Screenshot" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Boilerplate is Different
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🌍 Real Internationalization (Not Just a Dictionary)
&lt;/h3&gt;

&lt;p&gt;I'm talking about &lt;strong&gt;type-safe&lt;/strong&gt; translations that catch errors at compile time. No more broken translations in production because you typo'd a key.&lt;/p&gt;

&lt;p&gt;Here's what makes it special:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Three languages out of the box&lt;/strong&gt;: English, বাংলা (Bengali), and العربية (Arabic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTL support that actually works&lt;/strong&gt;: Arabic layouts automatically flip to right-to-left&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-simple language switching&lt;/strong&gt;: One click, zero page reload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe with TypeScript&lt;/strong&gt;: Your IDE will tell you when a translation is missing
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TypeScript knows all your translation keys&lt;/span&gt;
&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigation.home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ✅ Works&lt;/span&gt;
&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigation.homer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Compile error - typo caught!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔐 Role-Based Access Control That Scales
&lt;/h3&gt;

&lt;p&gt;Most tutorials show you basic auth and call it a day. But what about when you need different dashboards for users and admins? Or when you want to add a "Moderator" role later?&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Next.js 15's parallel routes&lt;/strong&gt; to make this painless:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
  (protected)/
    @admin/      # Admin-only views
      dashboard/
    @user/       # User views
      dashboard/
    layout.tsx   # Smart routing logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The layout automatically shows the right dashboard based on the user's role. No messy conditionals scattered everywhere. Want to add a new role? Just create a new parallel route folder. That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 A Design System That Doesn't Suck
&lt;/h3&gt;

&lt;p&gt;I'm using &lt;strong&gt;shadcn/ui&lt;/strong&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components are copy-paste ready (no bloated dependencies)&lt;/li&gt;
&lt;li&gt;Full TypeScript support&lt;/li&gt;
&lt;li&gt;Accessible by default (WCAG compliant)&lt;/li&gt;
&lt;li&gt;Easy to customize without fighting CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus &lt;strong&gt;next-themes&lt;/strong&gt; for light/dark mode with system preference detection and a manual toggle.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 ESLint That Actually Helps (Not Annoys)
&lt;/h3&gt;

&lt;p&gt;Let's be honest - most ESLint configs are either too strict or too loose. I spent time configuring rules that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Catch real bugs&lt;/strong&gt; (unused variables, missing dependencies, potential null references)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce consistency&lt;/strong&gt; (import order, naming conventions, formatting)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't get in your way&lt;/strong&gt; (no annoying warnings for things that don't matter)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work with Next.js 15&lt;/strong&gt; (proper App Router support, server component rules)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The config includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eslint-config-next&lt;/code&gt; - Official Next.js rules&lt;/li&gt;
&lt;li&gt;TypeScript-specific linting&lt;/li&gt;
&lt;li&gt;Import sorting and organization&lt;/li&gt;
&lt;li&gt;Best practices for React hooks&lt;/li&gt;
&lt;li&gt;Accessibility checks (a11y)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prettier&lt;/strong&gt; is wired up too (Tailwind plugin, format on save in &lt;code&gt;.vscode/settings.json&lt;/code&gt;). Run &lt;code&gt;npm run lint&lt;/code&gt; and &lt;code&gt;npm run prettier:fix&lt;/code&gt; for consistent, clean code.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 SEO Configuration That's Actually Usable
&lt;/h3&gt;

&lt;p&gt;Instead of hardcoding metadata everywhere, I created a single JSON configuration file that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Graph tags&lt;/li&gt;
&lt;li&gt;Twitter cards&lt;/li&gt;
&lt;li&gt;Structured data (JSON-LD)&lt;/li&gt;
&lt;li&gt;Multi-language meta tags&lt;/li&gt;
&lt;li&gt;Canonical URLs&lt;/li&gt;
&lt;li&gt;Dynamic sitemap generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just edit one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"appName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your App"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://yoursite.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"your"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"social"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"twitter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@yourhandle"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. SEO handled. The same config drives &lt;strong&gt;sitemap&lt;/strong&gt; (&lt;code&gt;sitemap.ts&lt;/code&gt;), &lt;strong&gt;robots.txt&lt;/strong&gt; (&lt;code&gt;robots.ts&lt;/code&gt;), and &lt;strong&gt;manifest&lt;/strong&gt; (&lt;code&gt;manifest.ts&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 Testing &amp;amp; CI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit and component tests&lt;/strong&gt;: Vitest + React Testing Library. Run &lt;code&gt;npm run test&lt;/code&gt; or &lt;code&gt;npm run test:watch&lt;/code&gt;; &lt;code&gt;npm run test:coverage&lt;/code&gt; for coverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E&lt;/strong&gt;: Playwright in &lt;code&gt;e2e/&lt;/code&gt;. Run &lt;code&gt;npm run e2e&lt;/code&gt; (dev server starts automatically); &lt;code&gt;npm run e2e:ui&lt;/code&gt; for the UI. Use &lt;code&gt;npm run e2e:webkit&lt;/code&gt; for WebKit-only (e.g. to save disk).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI&lt;/strong&gt;: GitHub Actions – &lt;code&gt;.github/workflows/check.yml&lt;/code&gt; runs lint, Prettier, tests, and build on push/PR; &lt;code&gt;.github/workflows/playwright.yml&lt;/code&gt; runs E2E.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔑 Authentication (NextAuth.js + Google OAuth)
&lt;/h3&gt;

&lt;p&gt;Auth is built in with &lt;strong&gt;NextAuth.js&lt;/strong&gt;. You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google OAuth&lt;/strong&gt; – enable by setting &lt;code&gt;GOOGLE_CLIENT_ID&lt;/code&gt;, &lt;code&gt;GOOGLE_CLIENT_SECRET&lt;/code&gt;, and &lt;code&gt;NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom login page&lt;/strong&gt; at &lt;code&gt;/auth/login&lt;/code&gt; with optional "Sign in with Google"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin role by email&lt;/strong&gt; – set &lt;code&gt;AUTH_ADMIN_EMAILS=admin@yourdomain.com&lt;/code&gt; (comma-separated); those Google accounts get the admin role automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT session&lt;/strong&gt; with role and user id; redirect to &lt;code&gt;/dashboard&lt;/code&gt; after sign-in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;, add your secrets, and you're done.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏥 Health Check
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET /api/health&lt;/code&gt; returns &lt;code&gt;{ status: "ok" }&lt;/code&gt; for load balancers and Kubernetes probes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started (The Real Way)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Clone and Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Grab the code&lt;/span&gt;
git clone https://github.com/salmanshahriar/nextjs-boilerplate-production-ready.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nextjs-boilerplate-production-ready

&lt;span class="c"&gt;# Install dependencies (use whatever you prefer)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# or bun install / yarn install / pnpm install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure Your Project
&lt;/h3&gt;

&lt;p&gt;This is where most boilerplates leave you hanging. Not this one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit &lt;code&gt;lib/config/app-main-meta-data.json&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"appName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Awesome SaaS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Revolutionary Product That Does X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We help Y achieve Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://myawesomesaas.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"organization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello@mycompany.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"social"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"twitter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@myhandle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/myhandle"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's your entire brand configuration. One file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Customize Your Languages (Optional)
&lt;/h3&gt;

&lt;p&gt;Want to add Spanish? Here's how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;locales/es.json&lt;/code&gt; with the same structure as &lt;code&gt;locales/en.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"common"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"appName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mi App"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"navigation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inicio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"about"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acerca de"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update &lt;code&gt;lib/config/app-main-meta-data.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"supported"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"locales"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"es"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Spanish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"nativeName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Español"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"locale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es_ES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ltr"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;lib/i18n/get-translations.ts&lt;/code&gt;, import &lt;code&gt;es.json&lt;/code&gt; and add &lt;code&gt;es&lt;/code&gt; to the &lt;code&gt;translations&lt;/code&gt; object. If you use strict translation keys, add the new locale to the &lt;code&gt;TranslationKeys&lt;/code&gt; union in &lt;code&gt;lib/i18n/types.ts&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Done. Your app now speaks Spanish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Set Up Your Roles
&lt;/h3&gt;

&lt;p&gt;The boilerplate comes with User and Admin roles. To add more:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new parallel route folder:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; app/&lt;span class="o"&gt;(&lt;/span&gt;protected&lt;span class="o"&gt;)&lt;/span&gt;/@moderator/dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add your pages inside that folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update &lt;code&gt;app/(protected)/layout.tsx&lt;/code&gt; to handle the new role:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moderator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moderator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's genuinely all you need to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt; and see your fully-configured app running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available Scripts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start development server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start production server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run lint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run ESLint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run lint:fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix ESLint errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unit tests (Vitest)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run test:watch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unit tests in watch mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run test:coverage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tests with coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run e2e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Playwright E2E tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run e2e:ui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Playwright with UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run e2e:webkit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;E2E in WebKit only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run prettier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run prettier:fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 6: Environment (First-Time Setup)
&lt;/h3&gt;

&lt;p&gt;Copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;. Set &lt;code&gt;NEXT_PUBLIC_APP_URL&lt;/code&gt; if you need to override the site URL (e.g. in production). For Google sign-in: set &lt;code&gt;NEXTAUTH_URL&lt;/code&gt;, &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt;, &lt;code&gt;GOOGLE_CLIENT_ID&lt;/code&gt;, &lt;code&gt;GOOGLE_CLIENT_SECRET&lt;/code&gt;, then &lt;code&gt;NEXT_PUBLIC_GOOGLE_AUTH_ENABLED=true&lt;/code&gt;. Optionally set &lt;code&gt;AUTH_ADMIN_EMAILS=admin@yourdomain.com&lt;/code&gt; so those emails get the admin role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18.17 or later&lt;/li&gt;
&lt;li&gt;npm, yarn, pnpm, or bun&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Project Structure (Explained for Humans)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
  (protected)/           # Routes behind auth
    @admin/             # Admin-only pages
    @user/              # User pages
    layout.tsx          # Role-based routing

  api/                  # API routes
    auth/[...nextauth]/ # NextAuth handler
    health/             # GET /api/health → { status: "ok" }
  auth/login/           # Login page
  unauthorized/         # 403-style page

  layout.tsx            # Root layout (theme, i18n)
  page.tsx              # Landing page
  not-found.tsx         # 404
  manifest.ts           # PWA manifest from config
  robots.ts             # Robots.txt from config
  sitemap.ts            # Dynamic sitemap from config

components/
  ui/                   # shadcn/ui
  layout/               # Header, sidebar, theme toggle
  language-switcher.tsx

locales/
  en.json, bn.json, ar.json

lib/
  auth/                 # NextAuth options, session, auth context
  config/
    app-main-meta-data.json
    site.ts             # baseUrl, supportedLocales
  i18n/
    get-translations.ts
    language-context.tsx
    use-translations.ts
    types.ts
  utils.ts

e2e/                    # Playwright E2E tests
.github/workflows/      # CI: check.yml, playwright.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What You Get Out of the Box
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Next.js 15&lt;/strong&gt; with App Router and Server Components&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;TypeScript&lt;/strong&gt; (strict mode)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Tailwind CSS&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;ESLint&lt;/strong&gt; and &lt;strong&gt;Prettier&lt;/strong&gt; (Next.js, TypeScript, a11y, format on save in .vscode)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;NextAuth.js&lt;/strong&gt; with optional Google OAuth and admin-by-email&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;i18n&lt;/strong&gt; with type safety and RTL (en, bn, ar)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;RBAC&lt;/strong&gt; with parallel routes (User / Admin)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;SEO&lt;/strong&gt; from one JSON config (metadata, sitemap, robots, manifest)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;next-themes&lt;/strong&gt; for dark mode (system + manual toggle)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;shadcn/ui&lt;/strong&gt; (accessible, customizable)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Vitest&lt;/strong&gt; + React Testing Library for unit/component tests&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Playwright&lt;/strong&gt; for E2E in &lt;code&gt;e2e/&lt;/code&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;GitHub Actions&lt;/strong&gt; for lint, format, test, build and E2E&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Health check&lt;/strong&gt; at &lt;code&gt;GET /api/health&lt;/code&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Vercel&lt;/strong&gt;-ready (one-click deploy from the repo)&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Talk: When Should You Use This?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Perfect for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS products with multiple user types&lt;/li&gt;
&lt;li&gt;International applications&lt;/li&gt;
&lt;li&gt;MVPs that need to look professional&lt;/li&gt;
&lt;li&gt;Projects where you want to ship fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maybe not ideal for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple landing pages (too much infrastructure)&lt;/li&gt;
&lt;li&gt;Projects with very specific auth requirements (you'll need to customize heavily)&lt;/li&gt;
&lt;li&gt;Apps that don't need i18n or role management&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned Building This
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parallel routes are underrated&lt;/strong&gt;: They make role-based routing so much cleaner than conditional rendering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe i18n is worth the setup&lt;/strong&gt;: Catching translation bugs at compile time saves hours of debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON configuration &amp;gt; hardcoded values&lt;/strong&gt;: When you can change your entire SEO strategy by editing one file, you move faster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplates should be opinionated&lt;/strong&gt;: Too many options = decision fatigue. I made the tough choices so you don't have to&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Contributing &amp;amp; Support
&lt;/h2&gt;

&lt;p&gt;Found a bug? Want to add a feature? The repo is fully open source:&lt;/p&gt;

&lt;p&gt;🐛 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready/issues" rel="noopener noreferrer"&gt;Report issues&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
⭐ &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready" rel="noopener noreferrer"&gt;Star on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🤝 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready/pulls" rel="noopener noreferrer"&gt;Submit a PR&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I built this because I got tired of setting up the same infrastructure for every project. If you're launching a product and don't want to spend two weeks on boilerplate, give it a try.&lt;/p&gt;

&lt;p&gt;It's saved me probably 30+ hours across my last three projects. Maybe it'll help you too.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your biggest pain point when starting a new Next.js project?&lt;/strong&gt; Drop a comment below - I'm always looking to improve this.&lt;/p&gt;

&lt;p&gt;Happy building! 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Links:
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://nextjs-boilerplate-production-ready.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/strong&gt; | 📦 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/strong&gt; | 🚀 &lt;strong&gt;&lt;a href="https://github.com/salmanshahriar/nextjs-boilerplate-production-ready/generate" rel="noopener noreferrer"&gt;Use this template&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://vercel.com/new/clone?repository-url=https://github.com/salmanshahriar/nextjs-boilerplate-production-ready" rel="noopener noreferrer"&gt;Deploy on Vercel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
