Best Cursor rules for a Next.js SaaS (copy-paste .mdc files)
By VibeBoot ·
Cursor is only as consistent as the context you give it. Without rules, every new chat re-decides where files go, which auth helper to call and whether to create a new database client. Project rules fix that. This guide explains how Cursor rules work today and gives you a set of rules for a Next.js SaaS (App Router, Auth.js, Prisma, Stripe) that you can copy and adapt.
How Cursor rules work
Rules live in .cursor/rules/ as .mdc files: Markdown with a small frontmatter block.
---
description: Rules for anything touching billing
globs: src/app/api/stripe/**,src/lib/stripe.ts
alwaysApply: false
---
# Stripe rules
- ...alwaysApply: true— included in every request. Use it for one core project rule.globs— attached automatically when files matching the pattern are in context. Use it for area-specific rules (billing, database).descriptiononly — the agent decides to pull it in when relevant.
Other agents don't read .cursor/rules, so also keep an AGENTS.md (read by many coding agents) and, if you use Claude Code, a CLAUDE.md with the same essentials.
What makes a rule good
- Specific and checkable. "Import the client as
import { db } from "@/lib/db"" beats "use good database practices". - Explains the trap. Agents follow a rule better when it says why: "never create a Stripe client at module top level (builds run without keys)".
- Short. Every always-on rule costs context on every request. Push detail into glob-scoped rules.
- Includes a verify step. "
npm run lintandnpm run buildmust pass before you finish" catches most agent mistakes.
Rule 1: project.mdc (always on)
---
description: Project stack, structure and conventions
alwaysApply: true
---
# Project rules
## Stack
- Next.js App Router (src/ dir), React Server Components by default, TypeScript strict, Tailwind CSS v4.
- Auth.js v5 with Prisma adapter. Stripe for subscriptions. Prisma ORM.
## File structure
- src/app/ routes; API routes in src/app/api/**/route.ts.
- src/auth.ts: import auth, signIn, signOut from "@/auth".
- src/lib/db.ts: Prisma singleton `db`. Never instantiate PrismaClient elsewhere.
- src/components/: shared UI.
## Conventions
- Prefer Server Components and Server Actions; "use client" only for interactivity.
- Protect pages server-side: const session = await auth(); if (!session?.user?.id) redirect("/login").
- Always scope DB queries to the signed-in user.
- Only NEXT_PUBLIC_* env vars may reach the browser. Add every new env var to .env.example.
- Validate user input on the server before writing to the DB.
## Verify
- npm run lint and npm run build must pass before you finish.
## Do not
- Do not swap libraries (auth, ORM, payments) without being asked.
- Do not commit .env files or real keys.The "Do not swap libraries" line is worth its weight: without it, an agent fixing an auth bug may decide to migrate you to a different auth library.
Rule 2: stripe.mdc (billing files only)
---
description: Rules for billing, Stripe and subscriptions
globs: src/app/api/stripe/**,src/lib/stripe.ts,src/app/pricing/**
alwaysApply: false
---
# Stripe rules
- Use getStripe() from "@/lib/stripe"; never create a Stripe client at module top level.
- Webhook must read the raw body with await req.text() and verify with stripe.webhooks.constructEvent.
- Handle new events with a new case in the webhook route; keep handlers idempotent.
- Stripe is the source of truth: update subscription fields only from the webhook.
- Redirect URLs use NEXT_PUBLIC_APP_URL.Each line here prevents a common agent mistake. For the full billing setup, see How to add Stripe subscriptions to a Next.js app with Cursor.
Rule 3: database.mdc (schema and db files only)
---
description: Rules for Prisma schema and database access
globs: prisma/**,src/lib/db.ts
alwaysApply: false
---
# Database rules
- Only import the client as import { db } from "@/lib/db".
- Keep Auth.js models (User, Account, Session, VerificationToken) compatible with @auth/prisma-adapter.
- New models: id String @id @default(cuid()), createdAt, updatedAt @updatedAt, and a userId relation with onDelete: Cascade.
- On SQLite, avoid enums, scalar lists and @db.* types.
- After schema changes run npx prisma db push (dev) or prisma migrate dev on Postgres.Add a "how to add a feature" recipe
The single most useful section in a project rule is a recipe the agent can follow for any feature:
- Model: add it to
prisma/schema.prisma, related toUserwithonDelete: Cascade, then push the schema. - Data access: query with
dbinside Server Components or Server Actions. - UI:
src/app/<route>/page.tsx; reusable pieces insrc/components/. - Mutations: Server Actions plus
revalidatePath(); route handlers only for webhooks. - Auth/billing: check
auth()first, then the subscription status for paid features. - Verify: lint and build.
With this in place, a prompt like "Add a Projects feature: users can create, rename and delete projects. Free users get 1 project, Pro unlimited." produces code that lands in the right files on the first try far more often.
Keep rules alive
- When you correct the agent twice about the same thing, add a rule.
- When you add a library or convention, update
project.mdcandAGENTS.mdin the same commit. - Delete rules that no longer match the code — stale rules are worse than none.
Skip the setup
The VibeBoot SaaS Starter ships with these three rule files plus AGENTS.md and CLAUDE.md, already matched to a working Next.js 16 + Auth.js + Prisma + Stripe codebase.