| name | whop-saas-starter |
| description | Guide for extending the Whop SaaS Starter v2 template (powered by whop-kit). TRIGGER when: adding dashboard pages, plan tiers, landing page sections, API routes, webhook handlers, or customizing auth/checkout/billing flows. Covers architecture conventions, file placement, plan system, config system, and component patterns specific to this codebase. |
Whop SaaS Starter v2 — Extension Guide
Use this skill when building on top of the Whop SaaS Starter v2 template. It explains how to extend the codebase — adding features, pages, plan tiers, and integrations — while following established patterns.
This template is powered by whop-kit — a framework-agnostic library that handles auth, payments, webhooks, and subscriptions. The template adds Next.js-specific wrappers via adapters in lib/adapters/.
Architecture at a Glance
whop-kit provides → core logic (JWT, OAuth, webhooks, config, subscriptions)
lib/adapters/ → framework-specific bridges (Next.js cookies, Prisma DB)
lib/*.ts → app-specific wrappers that combine whop-kit + adapters
Server Components → fetch data, pass props to Client Components
Layout protects routes → pages consume session via requireSession()
Plans are data-driven → edit definePlans() in constants.ts, everything adapts
Config is DB-backed → getConfig(key) with env var fallback (via whop-kit/config)
Webhooks use handler → createWebhookHandler() from whop-kit/webhooks
Quick Reference: Common Tasks
Add a new dashboard page
- Create
app/dashboard/your-page/page.tsx (server component)
- Call
requireSession() for auth (deduplicated by React.cache)
- Use
requirePlan("starter") if the page needs a minimum plan
- Add a nav item to
navItems in components/dashboard/sidebar.tsx
- Add a
loading.tsx skeleton in the same folder
See adding-pages.md for full details.
Add a new plan tier
- Add the key to
definePlans() in lib/constants.ts
- Key order = hierarchy (first = lowest, last = highest)
- Run the setup wizard or set env vars:
NEXT_PUBLIC_WHOP_{KEY}_PLAN_ID
Everything adapts automatically: pricing page, plan gating, setup wizard, config keys.
See plan-system.md for full details.
Add a new landing page section
- Create
components/landing/your-section.tsx (server component preferred)
- Follow the existing pattern:
<section> with border-t, max-w-5xl, py-24
- Import and place in
app/page.tsx in the desired order
- Current order: Hero → Features → Testimonials → Pricing → CTA
See landing-page.md for full details.
Add an API route
- Create
app/api/your-route/route.ts
- Always verify auth:
const session = await getSession() + check !session
- Admin-only routes: check
!session?.isAdmin
- Return
NextResponse.json(...) with appropriate status codes
See api-patterns.md for full details.
Handle a new webhook event
- Add the event handler to the
on object in app/api/webhooks/whop/route.ts
- The handler receives
(data, event) — data is the event payload, event includes the type
- Signature verification and JSON parsing are handled automatically by
createWebhookHandler()
your_new_event: async (data) => {
const userId = data.user_id as string;
},
See webhooks.md for full details.
Key Conventions
whop-kit Adapter Pattern
- Never import whop-kit directly in pages/components — use the wrappers in
lib/
- Adapters (
lib/adapters/next.ts, lib/adapters/prisma.ts) bridge whop-kit to Next.js/Prisma
- lib/*.ts files combine whop-kit functions with adapters and app-specific config
- If adding a new framework adapter, implement
CookieAdapter (from whop-kit/auth), DbAdapter (from whop-kit/subscriptions), or ConfigStore (from whop-kit/config)
Server vs Client Components
- Server by default — only add
"use client" when you need hooks, event handlers, or browser APIs
- Pass data down — server components fetch, client components receive via props
- No fetching in client components — use route handlers if client needs dynamic data
Styling
- Use CSS custom properties:
var(--background), var(--foreground), var(--accent), var(--muted), var(--border), var(--card), var(--surface)
- Use Tailwind with the bracket syntax:
bg-[var(--card)], text-[var(--muted)]
- Animations:
animate-slide-up, animate-fade-in, animate-scale-in with delay-100 through delay-500
- Respect
prefers-reduced-motion (already configured in globals.css)
Database
- Always use
select to fetch only needed fields
- Use
Promise.all for independent queries
- Use Prisma
upsert for create-or-update patterns
- Timestamps use
@db.Timestamptz(3) for timezone safety
- Pool size is 5 by default (serverless-friendly)
Session & Auth
getSession() — returns session or null, plan always fresh from DB (via whop-kit/auth + Prisma adapter)
requireSession() — redirects to /login if not authenticated
requirePlan("starter") — redirects to /pricing if plan insufficient
- Session is deduplicated per-request via
React.cache()
- API routes must always check auth themselves (don't rely on middleware)
Plan Gating
- Server:
requirePlan("starter") or hasMinimumPlan(session.plan, "starter")
- Client:
<PlanGate plan={session.plan} minimum="starter"> (pass plan from server parent)
- Plan hierarchy is auto-derived from key order in
definePlans()
CLI Management (whop-kit)
This project was scaffolded with create-whop-kit and can be managed with whop-kit:
Add pricing plans
npx whop-kit add plans
Add email
npx whop-kit add email
Add analytics
npx whop-kit add analytics
Deploy / redeploy
npx whop-kit deploy
Other commands
npx whop-kit status
npx whop-kit env
npx whop-kit catalog
npx whop-kit open whop
npx whop-kit open vercel
npx whop-kit upgrade
File Placement Rules
| What you're adding | Where it goes |
|---|
| Public marketing page | app/(marketing)/ |
| Protected dashboard page | app/dashboard/ |
| API endpoint | app/api/ |
| Landing section | components/landing/ |
| Dashboard component | components/dashboard/ |
| Framework adapter | lib/adapters/ |
| Shared utility | lib/ |
| Database schema change | db/schema.prisma |
| Documentation | content/docs/ |