name: next-forge
description: 'next-forge expert guidance — production-grade Turborepo monorepo SaaS starter by Vercel. Use when working in a next-forge project, scaffolding with npx next-forge init, or editing @repo/* workspace packages.'
metadata:
priority: 6
docs:
- "https://next-forge.com/docs"
- "https://github.com/haydenbleasel/next-forge"
pathPatterns:
- 'pnpm-workspace.yaml'
- 'apps/app/'
- 'apps/web/'
- 'apps/api/'
- 'apps/email/'
- 'apps/docs/'
- 'apps/studio/'
- 'apps/storybook/'
- 'packages/auth/'
- 'packages/database/'
- 'packages/design-system/'
- 'packages/payments/'
- 'packages/email/'
- 'packages/analytics/'
- 'packages/observability/'
- 'packages/security/'
- 'packages/ai/'
- 'packages/cms/'
- 'packages/collaboration/'
- 'packages/feature-flags/'
- 'packages/internationalization/'
- 'packages/notifications/'
- 'packages/rate-limit/'
- 'packages/seo/'
- 'packages/storage/'
- 'packages/webhooks/'
- 'packages/next-config/'
- 'packages/typescript-config/'
- '/keys.ts'
- '/env.ts'
- '/proxy.ts'
- 'biome.jsonc'
bashPatterns:
- '\bnext-forge\b'
- '\bnpx\s+next-forge\b'
- '\bpnpm\s+migrate\b'
- '\bpnpm\s+bump-deps\b'
- '\bpnpm\s+bump-ui\b'
- '\bprisma\s+(generate|db\s+push|format|studio)\b'
- '\bstripe\s+listen\b'
- '\bnpx\s+shadcn@latest\s+add\b.*-c\s+packages/design-system\b'
importPatterns:
- '@repo/auth'
- '@repo/database'
- '@repo/design-system'
- '@repo/payments'
- '@repo/email'
- '@repo/analytics'
- '@repo/observability'
- '@repo/security'
- '@repo/ai'
- '@repo/cms'
- '@repo/collaboration'
- '@repo/feature-flags'
- '@repo/internationalization'
- '@repo/notifications'
- '@repo/rate-limit'
- '@repo/seo'
- '@repo/storage'
- '@repo/webhooks'
- '@repo/next-config'
- '@t3-oss/env-nextjs'
- '@rescale/nemo'
promptSignals:
phrases:
- 'next-forge'
- 'next forge'
- '@repo/'
allOf:
-
- 'monorepo'
- 'saas'
- 'starter'
-
- 'turborepo'
- 'clerk'
- 'stripe'
anyOf:
- 'saas starter'
- 'production monorepo'
- 'keys.ts'
- 'pnpm-workspace'
noneOf:
- 'create-t3-app'
minScore: 6
next-forge
You are an expert in next-forge v5 — a production-grade Turborepo monorepo starter for SaaS applications, created by Vercel. It wires together 20+ packages (auth, database, payments, email, analytics, observability, security, AI, and more) into a cohesive, deploy-ready monorepo.
Monorepo Structure
├── apps/
│ ├── app/ # Main SaaS app (port 3000) — Clerk auth, route groups
│ ├── web/ # Marketing site (port 3001) — blog, pricing, i18n
│ ├── api/ # API server (port 3002) — webhooks, cron jobs
│ ├── email/ # React Email preview (port 3003)
│ ├── docs/ # Mintlify docs
│ ├── studio/ # Prisma Studio
│ └── storybook/ # Component dev (port 6006)
├── packages/
│ ├── ai/ # @repo/ai — AI SDK + OpenAI
│ ├── analytics/ # @repo/analytics — PostHog + GA + Vercel Analytics
│ ├── auth/ # @repo/auth — Clerk
│ ├── cms/ # @repo/cms — BaseHub
│ ├── collaboration/ # @repo/collaboration — Liveblocks
│ ├── database/ # @repo/database — Prisma + Neon
│ ├── design-system/ # @repo/design-system — shadcn/ui + Geist
│ ├── email/ # @repo/email — Resend + React Email
│ ├── feature-flags/ # @repo/feature-flags — Vercel Flags SDK
│ ├── internationalization/ # @repo/internationalization — next-international
│ ├── next-config/ # @repo/next-config — shared Next.js config
│ ├── notifications/ # @repo/notifications — Knock
│ ├── observability/ # @repo/observability — Sentry + BetterStack
│ ├── payments/ # @repo/payments — Stripe
│ ├── rate-limit/ # @repo/rate-limit — Upstash Redis
│ ├── security/ # @repo/security — Arcjet + Nosecone
│ ├── seo/ # @repo/seo — metadata + JSON-LD
│ ├── storage/ # @repo/storage — Vercel Blob
│ ├── typescript-config/ # @repo/typescript-config
│ └── webhooks/ # @repo/webhooks — Svix
├── turbo.json
├── pnpm-workspace.yaml
└── biome.jsonc # Biome via ultracite
Key principle: Packages are self-contained — they do not depend on each other. Apps compose packages.
Getting Started
npx next-forge@latest init # Scaffold project (interactive)
# Post-install:
pnpm migrate # prisma format + generate + db push
pnpm dev # Start all apps via turbo
Minimum env vars to run locally: DATABASE_URL + Clerk keys + app URLs. Everything else is optional.
Workspace Imports (@repo/*)
All packages use @repo/* workspace protocol with specific subpath exports:
// Auth
import { auth } from "@repo/auth/server";
import { ClerkProvider } from "@repo/auth/provider";
import { currentUser } from "@repo/auth/server";
// Database
import { database } from "@repo/database";
// Design System
import { Button } from "@repo/design-system/components/ui/button";
import { DesignSystemProvider } from "@repo/design-system";
import { fonts } from "@repo/design-system/lib/fonts";
// Payments
import { stripe } from "@repo/payments";
// Email
import { resend } from "@repo/email";
// Observability
import { log } from "@repo/observability/log";
import { captureException } from "@repo/observability/error";
// Analytics
import { AnalyticsProvider } from "@repo/analytics/provider";
// Security
import { secure } from "@repo/security";
// Feature Flags
import { createFlag } from "@repo/feature-flags";
// SEO
import { createMetadata } from "@repo/seo/metadata";
import { JsonLd } from "@repo/seo/json-ld";
// Storage
import { put } from "@repo/storage";
// AI
import { models } from "@repo/ai/lib/models";
Environment Variables
Type-safe env validation
Every package has a keys.ts using @t3-oss/env-nextjs + Zod. Apps compose them in env.ts:
// apps/app/env.ts
import { createEnv } from "@t3-oss/env-nextjs";
import { auth } from "@repo/auth/keys";
import { database } from "@repo/database/keys";
import { payments } from "@repo/payments/keys";
export const env = createEnv({ extends: [auth(), database(), payments()] });
Env file locations
| File | Purpose |
|---|---|
apps/app/.env.local | Main app env vars |
apps/web/.env.local | Marketing site |
apps/api/.env.local | API server |
packages/database/.env | DATABASE_URL |
Required env vars (minimum)
| Var | Package | Required for |
|---|---|---|
DATABASE_URL | database | Any database access |
CLERK_SECRET_KEY | auth | Authentication |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | auth | Client-side auth |
NEXT_PUBLIC_APP_URL | — | Cross-app linking |
NEXT_PUBLIC_WEB_URL | — | Cross-app linking |
NEXT_PUBLIC_API_URL | — | Cross-app linking |
Optional service env vars
| Service | Vars |
|---|---|
| Stripe | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET |
| Resend | RESEND_TOKEN, RESEND_FROM |
| PostHog | NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST |
| Sentry | NEXT_PUBLIC_SENTRY_DSN, SENTRY_ORG, SENTRY_PROJECT |
| BetterStack | BETTERSTACK_API_KEY, BETTERSTACK_URL |
| BaseHub | BASEHUB_TOKEN |
| Arcjet | ARCJET_KEY |
| Liveblocks | LIVEBLOCKS_SECRET |
| Knock | KNOCK_SECRET_API_KEY, NEXT_PUBLIC_KNOCK_API_KEY |
| Upstash | UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN |
| Vercel Blob | BLOB_READ_WRITE_TOKEN |
| Svix | SVIX_TOKEN |
| OpenAI | OPENAI_API_KEY |
| Feature Flags | FLAGS_SECRET |
Middleware / Proxy Pattern
next-forge uses proxy.ts (Next.js 16+), NOT middleware.ts:
// apps/app/proxy.ts — Clerk auth + Nosecone security
import { clerkMiddleware } from "@clerk/nextjs/server";
import { createMiddleware as createNosecone } from "@nosecone/next";
export default clerkMiddleware(createNosecone());
// apps/web/proxy.ts — Clerk + i18n + Arcjet + Nosecone, composed with nemo
import { compose } from "@rescale/nemo";
export default compose([clerkMiddleware, i18nMiddleware, arcjetMiddleware, noseconeMiddleware]);
Database (Prisma + Neon)
- Schema:
packages/database/prisma/schema.prisma - Client:
import { database } from "@repo/database" - Config:
packages/database/prisma.config.ts(Neon adapter) - Commands:
pnpm migrate(format + generate + db push) - Studio:
apps/studio(Prisma Studio)
Key Commands
| Command | Purpose |
|---|---|
pnpm dev | Start all apps |
pnpm dev --filter app | Start single app |
pnpm build | Build all |
pnpm migrate | Prisma format + generate + db push |
pnpm bump-deps | Update all dependencies |
pnpm bump-ui | Update shadcn/ui components |
pnpm run boundaries | Check Turborepo boundaries |
npx next-forge@latest update | Update next-forge (diff-based) |
npx shadcn@latest add <comp> -c packages/design-system | Add UI component |
stripe listen --forward-to localhost:3002/webhooks/payments | Local Stripe webhooks |
Deployment to Vercel
Deploy as 3 separate Vercel projects (app, api, web), each with Root Directory set to apps/<name>:
- Create project → set Root Directory to
apps/app - Add environment variables (use Team Environment Variables to avoid duplication)
- Repeat for
apps/apiandapps/web
Recommended subdomains: app.yourdomain.com, api.yourdomain.com, www.yourdomain.com
API App Patterns
apps/api/
├── app/
│ ├── cron/ # Cron jobs (GET handlers)
│ │ └── keep-alive/route.ts
│ ├── webhooks/ # Inbound webhooks
│ │ ├── auth/route.ts # Clerk webhooks
│ │ └── payments/route.ts # Stripe webhooks
│ └── health/route.ts
└── vercel.json # Cron schedules
Design System
- shadcn/ui (New York style, neutral palette)
- Geist Sans + Geist Mono fonts
- Tailwind CSS v4 + tw-animate-css
- Components at
packages/design-system/components/ui/ - Add components:
npx shadcn@latest add <name> -c packages/design-system
Common Gotchas
- Env vars are validated at build time — optional services still require env vars if the package is imported. Remove the import or provide a value.
- Multiple .env file locations — each app and the database package have separate env files. There is no single root
.env. pnpm migratebefore first run — without this, you get "table does not exist" errors.- Clerk webhooks cannot be tested locally — need a staging deployment.
- Heavy middleware imports → edge function >1MB on Vercel. Keep proxy.ts imports light.
- Prisma v7: use
--confignot--schemaforprisma studio. - next-forge is a boilerplate, not a library — updates via
npx next-forge updateneed manual merge with your changes. turbo.jsonglobalEnv — when adding new env vars used at build time, declare them inturbo.jsonglobalEnvor they won't invalidate cache.
Removing Optional Services
To remove an unused service (e.g., Stripe, BaseHub, Liveblocks):
- Delete the package directory (
packages/<service>/) - Remove all
@repo/<service>imports from apps - Remove the
keys()call from each app'senv.ts - Remove the workspace entry from
pnpm-workspace.yamlif needed - Run
pnpm installto clean lockfile
Migration Alternatives
| Category | Default | Alternatives |
|---|---|---|
| Auth | Clerk | Auth.js, Better Auth, Supabase Auth |
| Database | Prisma + Neon | Drizzle, Supabase, PlanetScale, Turso |
| Payments | Stripe | Lemon Squeezy, Paddle |
| CMS | BaseHub | Content Collections |
| Docs | Mintlify | Fumadocs |
| Feature Flags | Vercel Flags | Hypertune |
| Storage | Vercel Blob | UploadThing |
| Formatting | Biome | ESLint |
Cross-references
=> skill: turborepo — Monorepo configuration, caching, remote cache => skill: auth — Clerk setup, middleware patterns, sign-in/up flows => skill: payments — Stripe integration, webhooks, pricing => skill: email — Resend + React Email templates => skill: shadcn — shadcn/ui components, theming, CLI => skill: observability — Sentry + logging setup => skill: vercel-storage — Blob, Neon, Upstash => skill: vercel-flags — Feature flags with Vercel Flags SDK => skill: ai-sdk — AI SDK integration => skill: bootstrap — Project bootstrapping flow