StartupKitstartupkit
Getting Started

Scaffold a SaaS App

Create a production-ready SaaS project from the StartupKit template

Scaffold a blank SaaS application — Next.js 16 on Cloudflare Workers, auth on D1, Stripe billing, transactional email — with one command. This is the same flow the CLI's init command performs on your machine.

1. Scaffold the project

npx startupkit init "My SaaS"
cd my-saas

This creates the project folder from 01-studio/startupkit/templates/repo, initializes a fresh git history, runs bun install, and installs the startupkit agent skill so your AI agents know how to operate the stack.

What's inside:

  • Next.js 16 App Router served by a single Cloudflare Worker (OpenNext)
  • better-auth — email OTP sign-in + optional Google OAuth, sessions on Cloudflare D1
  • Stripe billing — checkout, customer portal, webhook subscription sync
  • Transactional email — Cloudflare Email binding + React Email templates
  • shadcn-style UI, Tailwind 4, next-intl, vitest, CI workflow
  • /kit listing kit — paste-ready directory copy, brand assets (logo, OG image, X/LinkedIn/Facebook/YouTube banners, screenshots), and a submission tracker. Public by URL, never indexed

Options: --dir <path> (destination), --repo <user/repo[#branch]> (template source), --no-install (skip bun install).

Prefer plain degit?

npx degit 01-studio/startupkit/templates/repo my-saas

2. Local environment

cd apps/web
cp .env.example .env.local
cp .dev.vars.example .dev.vars
openssl rand -base64 32

Put the generated value as BETTER_AUTH_SECRET in both files — they must match or sessions will randomly fail.

3. Create the database

The template uses Cloudflare D1 (serverless SQLite):

bunx wrangler d1 create startupkit-db

Copy the returned database_id into wrangler.jsonc and wrangler.next-dev.jsonc, then apply the migrations locally:

bun run migrate

4. Run it

bun run dev
  • App → http://localhost:3000
  • Email preview → http://localhost:3001 (React Email dev server)

Sign up with any email — locally the OTP prints to the dev server log instead of being emailed.

5. Wire up billing (optional)

  1. Create a recurring "Pro" product in Stripe (monthly + yearly prices)
  2. Set STRIPE_SECRET_KEY, STRIPE_PRICE_PRO_MONTHLY, STRIPE_PRICE_PRO_YEARLY, and STRIPE_WEBHOOK_SECRET in .env.local
  3. Point a Stripe webhook at https://<your-domain>/api/stripe/webhook for checkout.session.completed and customer.subscription.*

Upgrade buttons appear on /settings once STRIPE_SECRET_KEY is set.

6. Deploy

cd apps/web
bunx wrangler secret put BETTER_AUTH_SECRET
bunx wrangler secret put BETTER_AUTH_URL     # https://yourdomain.com
bun run migrate:remote                       # prod D1 migrations
bun run deploy                               # build + upload + shift traffic

Add your domain to the routes block in wrangler.jsonc for a custom domain. Verify a sending domain in Cloudflare Email Service so transactional email delivers (the EMAIL binding is pre-declared).

7. Push to GitHub

gh repo create <org>/<name> --private --source . --push

CI (lint, typecheck, test) runs automatically on every push.

Enable Dependabot security updates — version updates already come from the template's .github/dependabot.yml, but security updates (PRs that fix vulnerable dependencies) are per-repo settings and off by default for private repos:

gh api -X PATCH repos/<org>/<name> \
  -f security_and_analysis[dependabot_alerts]=enabled \
  -f security_and_analysis[dependabot_security_updates]=enabled

8. Rebrand the listing kit

/kit ships describing StartupKit itself so it is complete out of the box. Make it yours before you start submitting to directories:

  1. apps/web/lib/site.ts — name, URL, one-liner, tagline (≤10 words), short line (≤60 chars), category, socials. This also drives the site's <meta description> and Open Graph / Twitter card tags.
  2. apps/web/app/kit/page.tsx — rewrite the long-description tiers (Launch, SaaS, B2B review, AI directories, Dev) and the Submit-next rows for your category.
  3. Swap apps/web/public/favicon.svg, then regenerate assets with Chrome headless:
bun run kit:assets --skip-screenshots                                      # logos, OG image, social banners
KIT_BASE_URL=http://localhost:3000 bun run kit:assets --only=screenshots   # with the app running

bun run test guards the ≤60 / ≤10 constraints and rejects placeholder text.

What's next

  • Read the scaffold's own README.md — full setup reference
  • CLI Reference — research commands + auth
  • MCP Setup — give your agents research tools

On this page