DEV Community

Sanjana-03
Sanjana-03

Posted on

I Built a Production-Ready Next.js SaaS Starter Kit in 2 Weekends — Here's Everything I Included

Every time I started a new SaaS project, I found myself spending the first week doing the exact same thing — setting up authentication, connecting a database, wiring up payments, building a dashboard layout. The actual product hadn't even started yet and I'd already burned 7 days.

So I decided to build it once, do it properly, and package it as a reusable starter kit. Two weekends later, it was done. Here's exactly what I built, the decisions I made, and the problems I ran into.


The Stack

I wanted a stack that was modern, well-documented, and something most developers would actually want to use.

Next.js 14 (App Router)
The App Router is the future of Next.js. Server components, nested layouts, and file-based routing make it the obvious choice for any new SaaS project in 2024.

Clerk (Authentication)
I chose Clerk over NextAuth for one simple reason — it just works. Pre-built sign in, sign up, user management UI, webhook support, and it looks great out of the box. NextAuth requires a lot more configuration to get the same result.

Prisma + Supabase (Database)
Prisma gives you type-safe database queries in TypeScript without writing raw SQL. Supabase gives you a free hosted PostgreSQL database with a great dashboard. Together they're the fastest way to get a production database running.

Tailwind CSS + Shadcn/ui (Styling)
Tailwind for utility-first styling, Shadcn for pre-built accessible components. No custom CSS, no fighting with component libraries. Just clean, consistent UI.

Stripe (Payments)
Stripe is the industry standard for SaaS billing. Subscription management, customer portal, webhooks — everything you need is already there.

Vercel (Deployment)
One-click deploy from GitHub. Automatic SSL, edge network, and preview deployments on every push. Built by the same team as Next.js so compatibility is perfect.


What's Included

Authentication Flow

Clerk handles the entire auth flow. Sign up, sign in, forgot password, user profile — all pre-built. Protected routes are handled via middleware so the dashboard is completely locked down.

// middleware.ts
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher(["/dashboard(.*)"]);

export default clerkMiddleware((auth, req) => {
  if (isProtectedRoute(req)) auth().protect();
});
Enter fullscreen mode Exit fullscreen mode

After sign in, users are automatically redirected to the dashboard. After sign out, they go back to the landing page. Zero configuration needed.

Database Schema

Two models cover everything you need to get started:

model User {
  id           String        @id @default(cuid())
  clerkId      String        @unique
  email        String        @unique
  name         String?
  createdAt    DateTime      @default(now())
  subscription Subscription?
}

model Subscription {
  id                     String    @id @default(cuid())
  userId                 String    @unique
  stripeCustomerId       String    @unique
  stripeSubscriptionId   String?
  stripePriceId          String?
  status                 String    @default("inactive")
  currentPeriodEnd       DateTime?
  user                   User      @relation(fields: [userId], references: [id])
}
Enter fullscreen mode Exit fullscreen mode

When a user signs up via Clerk, a webhook fires and saves them to the database. When they upgrade via Stripe, the subscription record is created and linked to their user.

Dashboard Layout

The dashboard has a responsive sidebar with navigation, a top navbar with user avatar and dropdown, and mobile hamburger menu support. Built with Shadcn components so everything is accessible and keyboard-navigable.

app/(dashboard)/
  layout.tsx        ← Sidebar + navbar shell
  page.tsx          ← Overview with stat cards
  billing/page.tsx  ← Current plan + upgrade
  settings/page.tsx ← Profile + preferences
Enter fullscreen mode Exit fullscreen mode

Stripe Integration

Three API routes handle the entire billing flow:

  • /api/stripe/checkout — creates a Stripe checkout session
  • /api/stripe/portal — opens the customer billing portal
  • /api/webhooks/stripe — handles subscription events

The webhook handler listens for checkout.session.completed, customer.subscription.updated, and customer.subscription.deleted — updating the database accordingly so your app always knows the user's current plan status.

Landing Page

A complete marketing page with:

  • Navbar with sign in / get started CTAs
  • Hero with headline and subheadline
  • Features section (6 cards)
  • Pricing table (Free / Pro / Business)
  • FAQ accordion
  • Footer with links

Fully responsive and dark mode ready via next-themes.

Folder Structure

app/
  (marketing)/      ← Landing page
  (auth)/           ← Sign in / sign up
  (dashboard)/      ← Protected dashboard
components/
  ui/               ← Shadcn components
  marketing/        ← Landing page sections
  dashboard/        ← Sidebar, stat cards, etc.
lib/
  stripe.ts         ← Stripe client + helpers
  db.ts             ← Prisma client
  subscription.ts   ← getUserSubscriptionPlan()
prisma/
  schema.prisma
Enter fullscreen mode Exit fullscreen mode

The Hardest Parts

Prisma on Vercel

This one caught me off guard. Everything worked perfectly locally but the Vercel deployment kept failing with:

Module '"@prisma/client"' has no exported member 'PrismaClient'
Enter fullscreen mode Exit fullscreen mode

The fix was simple but not obvious — Vercel doesn't run prisma generate automatically. You have to add it to your build script:

"scripts": {
  "build": "prisma generate && next build",
  "postinstall": "prisma generate"
}
Enter fullscreen mode Exit fullscreen mode

The postinstall script runs after npm install on Vercel, generating the Prisma client before the build starts. One line fix, but it took a while to figure out.

Syncing Clerk Users to the Database

Clerk handles authentication but your database knows nothing about new users until you tell it. The solution is a Clerk webhook that fires on user.created and writes to your database:

// app/api/webhooks/clerk/route.ts
if (eventType === "user.created") {
  await db.user.create({
    data: {
      clerkId: event.data.id,
      email: event.data.email_addresses[0].email_address,
      name: `${event.data.first_name} ${event.data.last_name}`
    }
  })
}
Enter fullscreen mode Exit fullscreen mode

Without this, Stripe has no user to attach a subscription to when someone upgrades.

Stripe Webhook Reliability

Stripe webhooks need to be verified to prevent fake requests. Every webhook handler needs this check before processing anything:

const body = await req.text()
const signature = headers().get("stripe-signature")!

const event = stripe.webhooks.constructEvent(
  body,
  signature,
  process.env.STRIPE_WEBHOOK_SECRET!
)
Enter fullscreen mode Exit fullscreen mode

Skipping this verification is a security hole. The starter kit handles it correctly out of the box.


What I Would Do Differently

Start with the webhook setup earlier. I built the entire dashboard before setting up the Clerk → database sync, which meant none of my test users were in the database. Set up webhooks on day one.

Use Lemon Squeezy instead of Stripe if you're outside the US. Stripe has restrictions in some countries. Lemon Squeezy works globally, handles VAT automatically, and has a very similar API.

Add dark mode from the start. I added it at the end and had to touch almost every component. Building with dark: classes from day one saves a lot of time.


What's Next

I'm planning to add:

  • Email notifications via Resend
  • Team/organization support
  • Usage-based billing support
  • More dashboard page templates

Try It Yourself

If you want to skip 2 weeks of boilerplate setup and start building your actual product on day one, I've packaged everything into a ready-to-use starter kit.

Live demo: https://saas-template-fzx94d96j-raghuneha2803-5957s-projects.vercel.app/

Get the kit: https://raghuneha.gumroad.com/l/kvxtj

Includes full source code, README with step-by-step setup instructions, and deploys to Vercel in one click.

Happy to answer any questions in the comments. What stack are you using for your SaaS projects?

Top comments (0)