DEV Community

Cover image for How I Built a Production-Ready SaaS Boilerplate with Next.js 16
Lambda Studio
Lambda Studio

Posted on

How I Built a Production-Ready SaaS Boilerplate with Next.js 16

Every time I started a new SaaS project, I spent the first two weeks building the same things: authentication, billing, a dashboard, user settings, CRUD operations. Over and over again.

So I decided to build it once, properly, and turn it into a reusable starter kit. Here's how I built Lambda Kit — a production-ready SaaS boilerplate with Next.js 16.

The Problem

If you've ever tried to launch a SaaS, you know the drill. Before you can write a single line of your actual product, you need:

  • Auth with sign up, sign in, password recovery, and avatar upload
  • A billing system with multiple plans and a customer portal
  • A dashboard with a responsive sidebar
  • User settings with profile management
  • A CRUD example so you can replicate the pattern for your own entities
  • Webhooks to keep everything in sync
  • Dark mode (because of course)

That's easily 40-60 hours of work before you even start on what makes your SaaS unique.

The Stack

I chose each tool for a specific reason:

  • Next.js 16 — App Router with Server Components and Server Actions. No client-side JavaScript where it's not needed.
  • Clerk — Auth that just works. User management, social logins, and webhook sync out of the box.
  • Supabase — Postgres database with a generous free tier. Perfect for getting started.
  • Prisma — Type-safe database access. The developer experience is unmatched.
  • Stripe — The standard for subscription billing. Three plans (Free, Pro, Enterprise) with the Customer Portal handling upgrades, downgrades, and cancellations.
  • shadcn/ui — Beautiful, accessible components that you own. No vendor lock-in.
  • Tailwind CSS — Utility-first styling that keeps everything consistent.

What I Built

Authentication

Clerk handles the heavy lifting, but I added a custom profile page with Server Actions instead of using Clerk's pre-built components. Why? Because the buyer of this boilerplate needs to see the pattern — how to build forms with Server Components, how to handle image uploads, how to update user data server-side.

One lesson I learned the hard way: never capture complex SDK objects inside a Server Action closure. Always extract primitive values first.

// ❌ This breaks — user is a Clerk class, not serializable
async function updateProfile(formData: FormData) {
  "use server"
  await client.users.updateUser(user.id, { ... })
}

// ✅ This works — userId is a plain string
const userId = user.id
async function updateProfile(formData: FormData) {
  "use server"
  await client.users.updateUser(userId, { ... })
}
Enter fullscreen mode Exit fullscreen mode

Small detail, but it cost me a few hours of debugging.

Billing

Three plans: Free ($0), Pro ($19/mo), and Enterprise ($49/mo). The billing page shows the current plan, renewal date, payment method, and invoice history — all pulled from Stripe's API at render time.

The key insight: you don't need to build upgrade/downgrade UI. Stripe's Customer Portal handles all of that. One API call to create a portal session and redirect — done.

For the plan selection, I built a responsive modal that uses a Dialog on desktop and a Drawer on mobile. It detects the screen size with a custom useMediaQuery hook and renders the right component.

Dashboard

The dashboard follows a clean pattern:

  • Overview — Stats cards with real data from Prisma (total projects, active projects, current plan)
  • Projects — A full CRUD example with DataTable powered by TanStack Table
  • Settings — Nested layout with sidebar navigation (Profile, Billing)

The CRUD example is intentionally generic. It's called "Projects" but the buyer renames it to whatever their SaaS needs. The important thing is that every pattern is demonstrated: listing with server-side data fetching, creation with Server Actions, detail pages with dynamic routes, editing, and deletion with confirmation dialog.

Webhooks

Two webhook handlers keep everything in sync:

  • Clerk webhook — When a user signs up, it creates a record in Supabase. When they update their profile, it syncs. When they delete their account, it cleans up.
  • Stripe webhook — Handles checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, and invoice.payment_failed.

Without these, the app is broken. A user could pay for Pro but the database would still show Free. Webhooks are the glue.

Settings with Nested Routes

Instead of tabs, I used a layout with nested routes. Each settings section has its own URL (/dashboard/settings/profile, /dashboard/settings/billing), which means the browser back button works correctly and you can link directly to a specific section.

Lessons Learned

Server Components are the default. I only reach for "use client" when I absolutely need interactivity — file uploads, dropdowns, modals. Everything else stays on the server. The result is faster pages and simpler code.

Prisma's findUnique is strict. You can't pass multiple non-unique fields in the where clause. Use findFirst instead when filtering by id + userId.

Date hydration is tricky. Prisma returns Date objects, but passing them to Client Components causes hydration mismatches. Convert to ISO strings before passing data to the DataTable.

suppressHydrationWarning is your friend. Dark mode with next-themes always causes a hydration mismatch on the <html> tag. Adding suppressHydrationWarning is the official fix, not a hack.

What's Next

The boilerplate is live and I'm selling early access for $39. My roadmap includes:

  • Email templates with Resend
  • A public pricing page
  • MDX blog
  • More polish (loading skeletons, toast notifications, error boundaries)

The price will go up as features are added.

Try It

If you're tired of rebuilding the same SaaS foundation every time, Lambda Kit might save you a few weeks:

🔗 Live Demo: https://lambdakit-demo.vercel.app/
🛒 Get it on Gumroad: https://lambdastudioio.gumroad.com/l/iusks

I'd love to hear your feedback. What features do you consider essential in a SaaS boilerplate? What would you add?


Built by Lambda Studio from Patagonia, Argentina.

Top comments (0)