> free_tool --no-signup
Cursor rules generator
Pick your stack and get a .cursor/rules file with the things AI agents get wrong most often: Server Components by default, verified Stripe webhooks with the raw body, server-side auth checks, and no secrets in client code.
---
description: Stack, conventions and guardrails for this project (Next.js App Router with TypeScript (strict), Tailwind CSS v4, Auth.js v5, Prisma, Stripe, Zod)
globs:
alwaysApply: true
---
# Project rules for this project
Stack: Next.js App Router with TypeScript (strict) · Tailwind CSS v4 · Auth.js v5 · Prisma · Stripe · Zod. Package manager: npm. Do not swap these libraries for alternatives unless asked.
## How to work
- Before multi-file or structural changes, state a short plan (files to touch, approach) and then implement it.
- Keep diffs focused on the task. Don't reformat, rename or refactor unrelated code.
- Ask before adding a new dependency. Prefer what is already installed; if one is needed, install it with `npm install`.
- Match the existing patterns in the codebase before inventing new ones.
- Before finishing, make sure `npm run lint` and `npm run build` pass.
## Next.js App Router
- Components are React Server Components by default. Add `"use client"` only to components that need state, effects, event handlers or browser APIs, and keep those client components small and as low in the tree as possible.
- Fetch data in Server Components (async/await), not in `useEffect`. Pass only serializable props from server to client components.
- Routes live in `app/**/page.tsx`; shared UI in `layout.tsx`; loading and error states in `loading.tsx` / `error.tsx`; API endpoints in `app/**/route.ts` route handlers.
- In page, layout and route handler props, `params` and `searchParams` are Promises: `const { id } = await params`.
- Never use Pages Router APIs (`getServerSideProps`, `getStaticProps`, `pages/api`) in the `app/` directory.
- Use the Metadata API (`export const metadata` / `generateMetadata`) for titles and descriptions, `next/link` for internal links and `next/image` for images.
- Put server-only code (DB access, secret-using helpers) in modules that start with `import "server-only"` so it can never be bundled for the browser.
- Use Server Actions (`"use server"`) for mutations from your own UI; use route handlers for webhooks and external callers.
- Every Server Action is a public HTTP endpoint: re-check authentication and authorization inside each action and validate all arguments. Never rely on the UI hiding a button.
- After a mutation, call `revalidatePath()` or `revalidateTag()` (or `redirect()`) so stale data isn't shown.
## Security and secrets
- Secrets are read only on the server via `process.env`. Only variables prefixed `NEXT_PUBLIC_` reach the browser, so never give a secret that prefix and never import a secret-using module into a client component.
- Never hardcode keys, tokens or passwords, and never commit `.env*` files (except `.env.example`). Add every new env var to `.env.example` with a comment and a placeholder value.
- Treat all input (form data, params, search params, headers, webhook payloads) as untrusted. Validate on the server before using it.
- Authorize on the server for every read and write: check that the signed-in user owns the record they are accessing.
- Don't log secrets, tokens, full webhook payloads or personal data.
- Validate input with Zod schemas using `safeParse` in Server Actions and route handlers, and return a clear error on failure. Infer types with `z.infer` instead of duplicating them.
## Auth (Auth.js v5)
- Config lives in a root `auth.ts` that exports `{ handlers, auth, signIn, signOut }` from `NextAuth(...)`. Import these from that module; don't use v4 APIs like `getServerSession` or `authOptions`.
- The route handler is `app/api/auth/[...nextauth]/route.ts` with `export const { GET, POST } = handlers`. Don't add logic there.
- Get the session on the server with `const session = await auth()`. Protect pages with `if (!session?.user) redirect("/login")` in the page itself; middleware/proxy checks are an optimization, not the only guard.
- Env vars: `AUTH_SECRET` (generate with `npx auth secret`) plus provider vars named `AUTH_<PROVIDER>_ID` / `AUTH_<PROVIDER>_SECRET`, which Auth.js picks up automatically.
- Using the Prisma adapter: keep the adapter's User/Account/Session/VerificationToken tables compatible; you may add columns to User.
## Database (Prisma)
- `prisma/schema.prisma` is the single source of truth for models. Import one shared client (a singleton cached on `globalThis` in development); never call `new PrismaClient()` elsewhere.
- During development use `npx prisma migrate dev --name <change>` (or `prisma db push` while prototyping). In production run `npx prisma migrate deploy`. Never edit an applied migration.
- Run `npx prisma generate` after schema changes if the client isn't regenerated automatically.
- Query only in server code. Use `select` to return only the fields you need, scope queries to the current user (`where: { userId }`), and use `$transaction` for multi-step writes.
- New models: `id String @id @default(cuid())`, `createdAt DateTime @default(now())`, `updatedAt DateTime @updatedAt`, and relations with an explicit `onDelete`.
## Payments (Stripe)
- Use the Stripe secret key only on the server. Create the Stripe client lazily inside a helper (not at module top level) so builds work without keys.
- Never trust prices or amounts from the client. Look up the Stripe Price ID (or amount) on the server and create Checkout Sessions there. Stripe amounts are integers in the smallest currency unit (cents).
- Webhook route (`app/api/stripe/webhook/route.ts`): read the raw body with `await req.text()` (never `req.json()` before verifying) and verify it with `stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET)`. Return 400 when verification fails.
- The webhook route must not require a user session. Stripe calls it directly; the signature is the authentication.
- Make webhook handlers idempotent, because Stripe can deliver an event more than once or out of order. Upsert by Stripe IDs, or record processed `event.id`s. Return 2xx quickly.
- Treat Stripe as the source of truth for billing. Update subscription status in your database only from verified webhooks, not from the Checkout success redirect.
- Link Stripe customers to users with `metadata.userId` or `client_reference_id`, and store the `stripeCustomerId` on the user.
- Develop in test mode (`sk_test_...`) and forward events locally with `stripe listen --forward-to localhost:3000/api/stripe/webhook`. Test with card 4242 4242 4242 4242.
## Styling (Tailwind CSS v4)
- Tailwind v4 is configured in CSS: `@import "tailwindcss";` plus an `@theme { ... }` block for design tokens. There is no `tailwind.config.js` unless the project already has one; don't create it.
- Use utility classes in markup. Avoid custom CSS files and inline `style` except for truly dynamic values. Add design tokens to `@theme` rather than hardcoding hex colors everywhere.
- Build mobile-first: base classes for small screens, then `sm:` / `md:` / `lg:` overrides. Check that layouts work at about 375px wide.
- Keep it accessible: real `<button>` and `<a>` elements, visible focus styles, labels for inputs, and sufficient color contrast.
## Code style
- Use TypeScript strict mode. Avoid `any`; use `unknown` and narrow it. Type props and function return values at module boundaries.
- Use a single import alias (`@/`) instead of deep relative paths.
- Name files in kebab-case and components in PascalCase. Prefer named exports, except where Next.js requires a default export (`page`, `layout`, `route` files and similar).
- Handle errors explicitly. Show a useful message to users, and never expose stack traces or internal errors in responses.
## Do not
- Do not edit generated files, `node_modules`, `.next` or lockfiles by hand.
- Do not commit secrets or real API keys, or paste them into code or comments.
- Do not disable TypeScript or ESLint errors (`@ts-ignore`, `eslint-disable`) to make a build pass; fix the cause.
78 lines · generated in your browser, nothing is sent anywhere.
Want these rules already wired into a working app?
The VibeBoot SaaS Starter is a Next.js 16 project with Auth.js, Stripe subscriptions and Prisma, plus Cursor rules, AGENTS.md and CLAUDE.md that match the code.
See the SaaS Starter →Get better rules in your inbox
Occasional emails with new templates, rule updates and vibe-coding guides. No spam, unsubscribe anytime.
Subscribe to the newsletterFAQ
Where do I put the generated file?
Save it as .cursor/rules/project.mdc in the root of your project. Cursor loads rules from .cursor/rules automatically; this one uses alwaysApply: true so it's included in every request.
What is an .mdc file?
It's Markdown with a small frontmatter block (description, globs, alwaysApply) that tells Cursor when to attach the rule. You can split this file into several rules with their own globs, for example a Stripe rule that only applies to billing files.
Does it work with Claude Code or other agents?
The rules are plain Markdown. Paste the body (without the frontmatter) into AGENTS.md or CLAUDE.md for other agents.
Is anything sent to a server?
No. The file is generated in your browser. Nothing you select is stored or sent anywhere.