HomeBuild Guides › Build a SaaS App
Intermediate ⏱ 2-3 hours

Build a SaaS App

From zero to deployed SaaS with auth, payments, and email

Next.js
Next.jsFramework
Supabase
SupabaseDatabase & Auth
Stripe
StripePayments
Vercel
VercelHosting
Resend
ResendEmail

What You'll Build

A fully functional SaaS app with user authentication, subscription payments, transactional email, and production deployment.

Prerequisites

Architecture

Next.js handles the frontend and API routes. Supabase provides the Postgres database and user authentication. Stripe processes subscription payments via webhooks that hit your Next.js API routes. Resend sends transactional emails triggered by server-side events. Vercel deploys everything automatically on git push.

User → Next.js (Vercel) → Supabase (DB + Auth) → Stripe (Payments) → Resend (Email)

6 Steps

1
Next.js

Scaffold the Next.js project

~5 min

Create a new Next.js app with TypeScript, Tailwind CSS, and the App Router.

  1. Run the create command with TypeScript and Tailwind enabled
  2. Navigate into the project directory
  3. Start the dev server and confirm it loads at localhost:3000
Terminal
npx create-next-app@latest my-saas --typescript --tailwind --eslint --app --src-dir
cd my-saas
npm run dev
💡
Tip: Add "dev": "next dev --turbo" to your package.json scripts for faster development builds with Turbopack.
2
Supabase

Set up Supabase for database and auth

~15 min

Create a Supabase project, install the client libraries, and configure authentication.

  1. Go to supabase.com and create a new project
  2. Install the Supabase packages: @supabase/supabase-js and @supabase/ssr
  3. Copy your project URL and anon key from Settings → API
  4. Create a .env.local file with your credentials
  5. Create a Supabase client helper for browser and server usage
  6. Enable email/password auth in Authentication → Providers
Terminal
npm install @supabase/supabase-js @supabase/ssr
.env.local
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
src/lib/supabase.ts
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
💡
Tip: Enable Row Level Security (RLS) on all tables from day one. It's easier to set up now than to retrofit later.
3
Supabase

Build login and signup pages

~20 min

Create signup, login, and auth callback routes using Supabase Auth with server-side validation.

  1. Create a signup page at /app/signup/page.tsx with email and password fields
  2. Create a login page at /app/login/page.tsx
  3. Create an auth callback route at /app/auth/callback/route.ts for OAuth and email confirmation
  4. Add middleware to protect authenticated routes
  5. Create a dashboard page at /app/dashboard/page.tsx that requires login
src/app/auth/callback/route.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const code = searchParams.get('code')
  if (code) {
    const cookieStore = cookies()
    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      { cookies: { /* cookie handlers */ } }
    )
    await supabase.auth.exchangeCodeForSession(code)
  }
  return NextResponse.redirect(new URL('/dashboard', request.url))
}
💡
Tip: Add Google OAuth too - it takes 5 minutes in Supabase and dramatically increases signup rates.
4
Stripe

Add Stripe subscription billing

~30 min

Install Stripe, create products and prices in the dashboard, and build a checkout flow with webhooks.

  1. Install the Stripe SDK: npm install stripe
  2. Create a Stripe account and grab your test API keys
  3. Add Stripe keys to .env.local
  4. Create products and prices in the Stripe Dashboard (e.g., Free, Pro, Team plans)
  5. Build an API route at /api/checkout/route.ts that creates a Checkout Session
  6. Build a webhook handler at /api/webhooks/stripe/route.ts to process subscription events
  7. Sync subscription status to your Supabase profiles table
Terminal
npm install stripe
.env.local (add these)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
src/app/api/checkout/route.ts
import Stripe from 'stripe'
import { NextResponse } from 'next/server'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)

export async function POST(req: Request) {
  const { priceId, userId } = await req.json()
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
    metadata: { userId }
  })
  return NextResponse.json({ url: session.url })
}
💡
Tip: Use stripe listen --forward-to localhost:3000/api/webhooks/stripe to test webhooks locally.
⚠️
Warning: Never expose your STRIPE_SECRET_KEY on the client side. Only use it in server-side API routes.
5
Resend

Set up transactional email with Resend

~10 min

Configure Resend to send welcome emails, payment receipts, and other transactional messages.

  1. Sign up at resend.com and create an API key
  2. Install the Resend SDK: npm install resend
  3. Add your API key to .env.local
  4. Create a reusable email utility at src/lib/email.ts
  5. Trigger a welcome email from your auth callback when a user signs up
Terminal
npm install resend
src/lib/email.ts
import { Resend } from 'resend'

const resend = new Resend(process.env.RESEND_API_KEY)

export async function sendWelcomeEmail(email: string, name: string) {
  await resend.emails.send({
    from: 'Your SaaS <hello@yourdomain.com>',
    to: email,
    subject: `Welcome to the app, ${name}!`,
    html: `<h1>Welcome aboard!</h1><p>Your account is ready.</p>`
  })
}
💡
Tip: Verify your domain in Resend for better deliverability. Use their React Email integration for beautiful templates.
6
Vercel

Deploy to Vercel

~10 min

Push to GitHub and deploy your SaaS to production with environment variables and CI/CD.

  1. Push your project to a GitHub repository
  2. Go to vercel.com and import your repository
  3. Add all your environment variables (Supabase URL/key, Stripe keys, Resend key)
  4. Deploy - Vercel auto-detects Next.js and configures everything
  5. Set up your Stripe webhook endpoint to point to your production URL
  6. Test the full flow: signup → login → subscribe → receive email
Terminal
git init
git add .
git commit -m "Initial SaaS app"
git remote add origin https://github.com/you/my-saas.git
git push -u origin main
💡
Tip: Enable Vercel Analytics and Speed Insights for free performance monitoring from day one.
⚠️
Warning: Double-check that you're using live Stripe keys (not test keys) in your production environment variables.

🎉 You're Done!

A fully functional SaaS app with user authentication, subscription payments, transactional email, and production deployment.

Done for you

Want this built for you?

Get a step-by-step checklist, setup order, and the exact config for every tool in this guide. Or let me build it for you.

Get the checklist → Want this built for you?