# Cursor rules for Next.js, with real .mdc examples

> How Cursor rules work: the .mdc format, globs, alwaysApply and the four rule types, plus real Next.js rules from a generated repo that you can copy.

*Updated 2026-10-04.*

Cursor project rules are `.mdc` files in `.cursor/rules/`. Each one starts with frontmatter that decides when it loads: in every chat, when a file matches a glob, when the agent thinks it's relevant, or when you @-mention it. For a Next.js app, scope each rule to the folders it governs. Below: the format, Next.js globs, a template, and real rules from a generated repo.

## Where Cursor rules live

| Type | Where | Applies to |
|---|---|---|
| Project rules | `.cursor/rules/*.mdc`, committed to git | This repo |
| User rules | Cursor settings | Every project you open |
| Team rules | The Cursor dashboard | Every repo on the team |
| AGENTS.md | The repo root and any subfolder | This repo, as plain markdown |

When more than one applies, Cursor's order is Team Rules, then Project Rules, then User Rules.

Things worth knowing before you write one:

- **Folders work.** `.cursor/rules/frontend/components.mdc` is fine.
- **Plain `.md` is ignored.** A `.md` file in `.cursor/rules/` has no frontmatter, so the rules system skips it.
- **Rules are for Agent (Chat).** User Rules don't apply to Inline Edit (Cmd/Ctrl+K). No rule affects Cursor Tab.
- **`.cursorrules` is legacy.** Cursor says the root `.cursorrules` file "is legacy and will be deprecated." Move it into `.cursor/rules/`.

## The .mdc file format

An `.mdc` file is markdown with a frontmatter block on top. Three fields:

| Field | Value | What it does |
|---|---|---|
| `description` | A sentence | Tells the agent what the rule is for. Cursor uses it to decide relevance |
| `globs` | File patterns, comma-separated | Attaches the rule when a file you're working on matches |
| `alwaysApply` | `true` or `false` | `true` puts the rule in every chat |

Then the body: plain markdown. Bullets, headings, code samples. A minimal file-scoped rule for Next.js route handlers:

```markdown
---
description: Route handler rules for the App Router
globs: src/app/api/**/route.ts
alwaysApply: false
---

- Parse every body, query and route param with Zod before you use it.
- Check the session inside the handler. The proxy is not the boundary.
- Return typed JSON with a real status code. Never return a stack trace.
```

## The four rule types

Which type a rule is depends on its frontmatter.

| Type | Frontmatter | Loads | Next.js use |
|---|---|---|---|
| Always Apply | `alwaysApply: true` | Every chat session | Code style, git, security |
| Apply to Specific Files | `alwaysApply: false` plus `globs` | When a file matches | Webhooks, schema, components |
| Apply Intelligently | `alwaysApply: false` plus `description` | When the agent decides it's relevant | Procedures: add a page, run a migration |
| Apply Manually | `alwaysApply: false`, no description, no globs | When you @-mention it, like `@my-rule` | One-off checklists |

Default to file-scoped. An Always Apply rule rides along in every chat, even one about a CSS tweak. Save it for the few rules that really apply everywhere.

## Cursor rules globs for Next.js

Globs go on one `globs:` line, separated by commas. `**` matches any depth. Patterns that fit a Next.js App Router repo:

| Glob | Matches |
|---|---|
| `src/app/**/page.tsx` | Every page |
| `src/app/**/route.ts` | Every route handler |
| `src/app/api/webhooks/stripe/**` | The Stripe webhook route |
| `src/components/**` | Every component |
| `src/db/**` | Schema and queries |
| `tests/**,src/**/*.test.ts,src/**/*.test.tsx` | Unit and end-to-end tests |
| `next.config.ts,src/proxy.ts` | Build config and the request proxy |

One Next.js 16 detail. The `middleware` file convention is deprecated and renamed to `proxy`. A rule scoped to `middleware.ts` in a Next.js 16 repo guards a file that shouldn't exist.

Here is every rule in the generated example repo, with its globs. The `.mdc` version of each rule uses the same patterns, joined with commas. Rules marked "always loaded" become `alwaysApply: true`.

- `admin-access`: `src/app/(admin)/**`, `src/components/admin/**`, `src/lib/admin/actions.ts`, `src/app/api/admin/**`
- `admin-mutations`: `src/lib/admin/**`, `src/components/admin/**`, `scripts/admin/**`
- `app-shell`: `src/app/(app)/**`, `src/components/app/**`, `src/components/ui/sidebar.tsx`, `src/lib/app-shell.ts`, `src/lib/nav.ts`
- `auth-server-boundary`: `src/lib/auth/**`, `src/app/api/auth/**`, `src/app/(better-auth)/**`, `src/components/auth/**`
- `billing-core`: `src/lib/pricing.ts`, `src/lib/billing/**`, `src/app/pricing/**`, `src/app/(app)/billing/**`, `src/components/billing/**`
- `blog-content`: `content/**`
- `blog-rendering`: `src/app/blog/**`, `src/app/sitemap.ts`, `src/lib/blog/**`, `src/components/blog/**`, `src/mdx-components.tsx`
- `code-style`: always loaded
- `deployment`: `next.config.ts`, `vercel.json`, `package.json`, `src/proxy.ts`, `src/app/**/route.ts`, `.env.example`
- `drizzle-migrations`: `src/db/**`, `drizzle/**`, `drizzle.config.ts`
- `drizzle-schema`: `src/db/**`
- `email-sending-discipline`: `src/lib/email/**`, `src/app/api/webhooks/resend/**`, `src/lib/auth/**`, `src/lib/billing/**`
- `event-naming`: `src/lib/analytics/**`, `src/app/**`, `src/components/**`
- `git`: always loaded
- `identify-timing`: `src/lib/analytics/**`, `src/app/**`, `src/components/**`
- `landing-and-legal`: `src/lib/site.ts`, `src/lib/llms.ts`, `src/app/page.tsx`, `src/app/(legal)/**`, `src/app/llms.txt/**`, `src/components/marketing/**`, `src/components/site/**`
- `neon-connections`: `src/db/**`, `src/app/api/**`
- `neon-migrations`: `src/db/**`
- `roles-are-server-side`: `src/app/**`, `src/components/**`, `src/lib/auth/**`
- `security`: always loaded
- `sentry-capture`: `src/**`, `sentry.server.config.ts`, `sentry.edge.config.ts`
- `sentry-no-pii`: always loaded
- `server-truth-and-pii`: `src/lib/analytics/**`, `src/app/**`, `src/components/**`
- `stripe-billing-store`: `src/lib/billing/**`, `scripts/billing/**`
- `stripe-pricing-source-of-truth`: `src/lib/pricing.ts`, `src/lib/billing/**`, `scripts/billing/**`
- `stripe-server-boundary`: `src/lib/billing/**`, `src/app/api/webhooks/stripe/**`, `src/app/**`
- `stripe-webhook-integrity`: `src/app/api/webhooks/stripe/**`, `src/lib/billing/provider.ts`, `src/lib/billing/webhook.ts`
- `testing`: `tests/**`, `src/**/*.test.ts`, `src/**/*.test.tsx`
- `tokens-only`: `src/components/**`, `src/app/**`
- `ui-kit`: `src/components/**`, `src/app/**`

Notice the spread. Auth rules cover `src/lib/auth/**`. Billing rules cover `src/lib/billing/**`. The design rule covers components and pages. Each rule loads for the folders it governs, not for the whole repo.

## Cursor rules template for Next.js (copy this)

Two starter rules. We wrote them as skeletons for this page, not from a real project. Swap in your own paths and commands.

`.cursor/rules/nextjs.mdc`, on in every chat:

```markdown
---
description: Next.js App Router conventions for this repo
alwaysApply: true
---

- App Router only. Do not add a `pages/` directory.
- Server Components by default. Add "use client" only for state, effects, refs or browser APIs.
- Fetch data in Server Components or route handlers, not in a client effect.
- Import with `@/`. Never `../../../`.
- Before you finish: `pnpm typecheck` and `pnpm lint` pass.
```

`.cursor/rules/components.mdc`, scoped to UI files:

```markdown
---
description: Component rules
globs: src/components/**,src/app/**/*.tsx
alwaysApply: false
---

- Build from the components in `src/components/ui/`. No one-off buttons.
- Colors and spacing come from design tokens. No hex values, no raw palette classes.
- Every interactive element has an accessible name.

@src/components/ui/button.tsx
```

The last line references a real file instead of pasting it. Cursor pulls the file into context with the rule, so the example never goes stale.

## Real Cursor rules from a generated Next.js repo

The generator writes 58 `.mdc` files for the [Indie SaaS preset](/stack/indie-saas): one per rule, plus one per skill. That's Next.js with Better Auth, Neon, Drizzle, Stripe, Resend, PostHog, Sentry, an admin panel and an MDX blog. Here are three of them, one per rule type, straight from a real generate run.

### An Always Apply rule

```markdown
---
alwaysApply: true
description: Security rules
globs: "**"
---

The guard hooks in `.claude/hooks/` enforce a subset of this mechanically. The
rest is on you. Assume every one of these will be audited by the
`security-auditor` agent before a release.

## Secrets

- Real secret values live in `.env.local` and in the hosting provider's
  environment settings. Nowhere else. Not in `.env.example`, not in a test, not
  in a comment, not in a commit message, not in a log line, not in a doc.
- `.env.example` lists every key with a placeholder that is obviously fake and a
  comment saying where to get the real one.
- Never `echo`, `cat`, `console.log`, or interpolate a secret into a shell
  command, a URL, an error message, or an analytics event.
- Only `NEXT_PUBLIC_`-prefixed variables may be read from client code. A
  non-prefixed variable read in a `"use client"` file is a leak even if the code
  path looks unreachable.
```

*First 22 of 87 lines of `.cursor/rules/security.mdc`.*

`alwaysApply: true` with `globs: "**"`. Security applies to every file, so it rides along in every chat. The opening paragraph is honest about scope: hooks enforce part of it, and the rest is on the agent.

### A file-scoped rule

```markdown
---
alwaysApply: false
description: Verify every Stripe webhook, keep every handler idempotent
globs: src/app/api/webhooks/stripe/**,src/lib/billing/provider.ts,src/lib/billing/webhook.ts
---

The webhook route is an unauthenticated public endpoint. The signature is the
only thing between a Stripe event and a forged POST that grants a lifetime
plan.

The route is three lines: `processWebhook(billingProvider, request)`. The
pipeline in `src/lib/billing/webhook.ts` is shared by every provider, and the
Stripe parts are `verifyWebhook` and `translate` in `provider.ts`. Keep it that
way: nothing Stripe-specific in the route.

**Read the body as text.** `processWebhook` calls `request.text()` and hands
those exact bytes to `verifyWebhook`. `request.json()` reorders keys and every
signature check fails. Never add a body parser or middleware in front of it.
```

*First 18 of 68 lines of `.cursor/rules/stripe-webhook-integrity.mdc`.*

Three globs, one per file the rule governs: the webhook route, the provider adapter and the shared pipeline. It names real functions and tells the agent exactly what breaks (`request.json()` reorders keys). A reviewer can check the agent followed it. The full Stripe battery: [/with/stripe](/with/stripe).

### A skill as an Apply Intelligently rule

````markdown
---
alwaysApply: false
description: "Skill verify: Prove the repo is actually configured: every required env var present, every configured service reachable, and the guard hooks still blocking what they claim to block."
---

# verify

Answer one question with evidence: **is this checkout of `my-app` in a
state where it can run?** Not "does the dev server start": that succeeds with
half the services missing and fails two hours later inside a request handler.

Run this after a fresh clone, after adding a battery, after rotating a
credential, before a deploy, and any time a bug report starts with "it works on
my machine".
````

*First 14 of 111 lines of `.cursor/rules/skill-verify.mdc`.*

No globs. `alwaysApply: false`. Just a description that starts with "Skill verify:". Cursor's agent reads the description and pulls the rule in when the task fits. That's how the generator gives Cursor all 28 skills without loading any of them up front. The same skills in Claude Code: [the skills guide](/guides/claude-code-skills).

## Cursor rules best practices

| Practice | Why |
|---|---|
| Keep each rule under 500 lines | Cursor's own guidance. Every rule in the example repo fits |
| One topic per file, named for it | `stripe-webhook-integrity.mdc` beats `rules2.mdc` |
| Scope with globs before you reach for alwaysApply | Always-on rules load in every chat |
| Write the description as a trigger | The agent decides relevance from it |
| Reference files with `@` | Don't paste code that will drift |
| Show a wrong and a right example | One snippet beats three adjectives |
| Write checkable statements | "Read the body with `request.text()`" can be verified. "Handle webhooks carefully" can't |
| Commit `.cursor/rules/` | Project rules are version-controlled, so the whole team gets them |

## Common Cursor rules mistakes

- **Plain `.md` files in `.cursor/rules/`.** Ignored. Use `.mdc` with frontmatter.
- **Everything on `alwaysApply: true`.** Every chat pays for every rule.
- **`globs: "**"` on a narrow rule.** It matches every file, so a billing rule shows up for a CSS fix.
- **No description and no globs.** That's a manual rule. It loads only when you @-mention it, and you'll forget.
- **A leftover `.cursorrules`.** Create an Always Apply rule, paste the content in, then delete the old file.
- **Expecting rules in Cursor Tab.** Rules don't touch it, and User Rules skip Inline Edit too.
- **Old Next.js advice.** Rules copied from a pre-16 project mention `middleware.ts` and `pages/`. Check them against your version.
- **Rules as a safety net.** A rule is an instruction. It can't stop a command from running.

## Cursor rules vs AGENTS.md

Cursor reads both. AGENTS.md is plain markdown with no frontmatter, at the root or in any subfolder. Nested files combine with their parents, and the more specific one wins. Use it for basics every agent should know, because Codex and many other tools read it too. Use `.mdc` rules when you need globs, descriptions or manual rules. The full comparison: [CLAUDE.md vs AGENTS.md](/guides/claude-md-vs-agents-md). Real AGENTS.md files: [AGENTS.md examples](/guides/agents-md-examples).

## A Cursor rules boilerplate for Next.js

You can write all of this by hand. Or pick Cursor as a target at [/build](/build), and get a Next.js repo with `.cursor/rules/` already filled in for your stack. The same rules compile to `.claude/rules/` for Claude Code and to AGENTS.md for Codex, so the three never drift. Batteries bring their own rules: [Better Auth](/with/better-auth) adds auth boundaries, [Stripe](/with/stripe) adds webhook and pricing rules. Free during launch, then $99 once.

From a terminal: `npm create agentic-boilerplate@latest my-app`.

What each agent target gets: [the agentic layer](/docs/the-agentic-layer). The bugs these rules prevent, written up: [the cookbook](/cookbook). For the Claude Code side: [CLAUDE.md template](/guides/claude-md-template), [Claude Code hooks](/guides/claude-code-hooks), [Claude Code subagents](/guides/claude-code-subagents) and [Claude Code setup for Next.js](/guides/claude-code-setup-nextjs).

## FAQ

### Where do Cursor rules go?

In `.cursor/rules/` at the repo root, as `.mdc` files you commit. Subfolders inside `.cursor/rules/` work for keeping them organized.

### What is an .mdc file?

A markdown file with a frontmatter block on top. The frontmatter holds `description`, `globs` and `alwaysApply`, which decide when Cursor loads the rule.

### How do globs work in Cursor rules?

`globs` lists file patterns separated by commas, like `src/app/api/**,src/lib/billing/**`. With `alwaysApply: false`, the rule attaches when a file you're working on matches one of them.

### What does alwaysApply do in Cursor rules?

`alwaysApply: true` puts the rule in every Agent chat. Set it to `false` to load the rule by glob, by description, or only when you @-mention it.

### Is .cursorrules deprecated?

Cursor calls the root `.cursorrules` file legacy and says it will be deprecated. Move its content into an Always Apply rule in `.cursor/rules/`, then delete the old file.

### Does Cursor read AGENTS.md?

Yes. Cursor reads AGENTS.md at the root and in any subfolder, as plain markdown. Nested files combine with their parents, and the more specific instruction wins.


## Sources

- [Cursor docs: Rules](https://cursor.com/docs/context/rules)
- [Cursor help: Rules](https://cursor.com/help/customization/rules)

## Generate it

[Build your repo](https://agenticboilerplate.com/build)

---

Agentic Boilerplate: A Next.js repo your agent already knows. Free during launch, then $99 once.

- Site map for agents: https://agenticboilerplate.com/llms.txt
- Public API: https://agenticboilerplate.com/openapi.json
- Contact: agenticstudio@gmail.com
