Cursor Rules: Complete .mdc Guide with Templates (2026)

Cursor Rules: Complete .mdc Guide with Templates (2026)

Last updated: August 2026 · Covers Cursor 0.47+


Cursor Rules are project-level instructions that tell Cursor AI exactly how to write code, structure files, handle errors, and behave across every task in your project. Without them, Cursor falls back to generic model defaults — with them, every suggestion automatically follows your team's stack, conventions, and workflow preferences.

Cursor currently has two rule formats, and this matters more than it sounds: the legacy .cursorrules single file, and the current .cursor/rules/*.mdc system. This guide leads with the current format, explains exactly why the legacy format silently breaks in Agent mode, and gives you a migration path if you're still on the old one.


1. Two Formats: What Changed and Why It Matters

.cursor/rules/*.mdc — the current format

Introduced to replace the single-file system, .mdc (Markdown with Cursor metadata) files live in a .cursor/rules/ directory and support YAML frontmatter that controls when and how each rule activates — something the legacy format cannot do at all.

.cursorrules — the legacy format

A single flat file at your project root. It has no scoping, no activation logic, and loads unconditionally into every request — which was fine when it was the only option, but is now a real limitation.

The critical gotcha: .cursorrules is silently ignored in Cursor's Agent mode. Agent mode uses a different context-loading path than Chat and Composer, and the legacy file is not part of it. If your team has adopted Agent mode for refactoring, feature implementation, or autonomous multi-step tasks — which is now the default way most developers use Cursor — a .cursorrules-only project is providing zero guidance during those sessions, with no warning that this is happening.

You do not need to delete an existing .cursorrules file immediately — it still loads in Chat and Composer for backward compatibility. But for any project where Agent mode is used regularly, migrating to .mdc is not optional if you want rules to actually apply. For the full diagnostic and fix, see Cursor Rules Not Working.

For any new project in 2026, start with .mdc directly — there is no reason to begin with a format that will need migrating.


2. The .mdc Format: Structure and Activation Modes

File location

my-project/
├── .cursor/
│   └── rules/
│       ├── 01-core.mdc
│       ├── 02-typescript.mdc
│       └── 03-testing.mdc
├── package.json
└── src/

Files are loaded in lexicographical order — numeric prefixes let you control which rules Cursor "sees" first.

Frontmatter fields

Each .mdc file has a YAML frontmatter block with three fields:

---
description: Conventions for the API layer
globs: "src/api/**/*.ts"
alwaysApply: false
---

# API conventions
- All handlers validate input with Zod before processing.
- Return NextResponse.json() with an explicit status code.
  • description — a summary Cursor uses to decide relevance when the rule isn't force-loaded
  • globs — file path patterns that trigger auto-loading when matching files are in context
  • alwaysApply — boolean; true loads the rule into every single request regardless of context

Four activation modes

These three fields combine into four distinct behaviors:

Mode Configuration When it loads
Always alwaysApply: true Every request, no exceptions
Auto-attach alwaysApply: false + globs set Only when the agent is editing a matching file
Agent-decided alwaysApply: false + description, no globs The agent reads the description and decides if it's relevant
Manual No globs, no alwaysApply Only when explicitly referenced with @ruleName in chat

Use Always sparingly — for the 3–5 rules that are genuinely universal to the project (stack, language version, absolute constraints). Use Auto-attach for anything file-type or directory specific (React component conventions, API route conventions, test conventions). Use Agent-decided for rules that apply situationally in a way a glob can't capture (a security review checklist that should trigger when auth code is touched). Use Manual for reference material you want available but don't want consuming context by default.


3. Syntax Guidelines

  • Write in imperative sentences: "Always use named exports." not "Named exports should be preferred."
  • Be specific and measurable: "Functions must not exceed 40 lines." not "Keep functions short."
  • Avoid ambiguity: "Use async/await instead of .then() chains." not "Handle async properly."
  • Group related rules under Markdown headings — Cursor uses these as structural context.
  • Use bullet lists for sets of related constraints.
  • Use code blocks for exact patterns, naming conventions, or examples Cursor should follow.

Minimal working example

---
description: Core project identity — always loaded
alwaysApply: true
---

# Project Rules

## Language and environment
- TypeScript strict mode throughout the project.
- Node.js 20+. Use ES modules (`import`), not CommonJS (`require()`).

## File structure
- New components go in `src/components/`.
- Each component gets its own folder: `src/components/Button/Button.tsx` + `Button.test.tsx`.

## Code style
- Functional components only. No class components.
- Named exports for all components and utilities.
- No `any` types. Use `unknown` and narrow it.

## Task behaviour
- Before implementing, write a 3-step plan in a comment block.
- Run `npm test` after every implementation.
- If requirements are ambiguous, ask one clarifying question before starting.

4. Ready-to-Use .mdc Templates

Each template below is split into multiple .mdc files by concern — this is the current recommended pattern, replacing the old single-file approach.

Template 1: Next.js 14 App Router + TypeScript + Prisma

01-core.mdc (Always)

---
description: Project identity and stack
alwaysApply: true
---

# Next.js / TypeScript / Prisma

Next.js 14 App Router. TypeScript strict mode. PostgreSQL via Prisma.
Styling: Tailwind CSS only. Testing: Vitest + React Testing Library.
All routes in `app/`. Never use `pages/`.

02-components.mdc (Auto-attach)

---
description: React component conventions
globs: "src/components/**/*.tsx"
alwaysApply: false
---

- Functional components only. Named exports.
- No `any` type — use `unknown` and narrow it.
- Server components by default; add `'use client'` only when required.

03-data-access.mdc (Auto-attach)

---
description: Prisma and API conventions
globs: ["src/services/**/*.ts", "app/api/**/*.ts"]
alwaysApply: false
---

- All Prisma queries go through service functions in `src/services/`.
- Service functions return `{ data: T } | { error: string }`.
- Never call `prisma` directly inside a component, page, or route handler.
- API success: `{ success: true, data: T }`. API error: `{ success: false, error: string }`.

Template 2: React + TypeScript SPA (Vite + Zustand + React Query)

01-core.mdc (Always)

---
description: Project stack
alwaysApply: true
---

React 18, TypeScript strict, Vite. State: Zustand (client) + React Query (server).
Styling: Tailwind CSS only. No inline styles, no CSS Modules.

02-state.mdc (Auto-attach)

---
description: State management conventions
globs: ["src/stores/**/*.ts", "src/hooks/**/*.ts"]
alwaysApply: false
---

- Zustand stores in `src/stores/`, one store per domain.
- Server state via React Query only — never duplicate in Zustand.
- No `useEffect` for data fetching — use React Query.
- Zustand actions defined inside the store, not as standalone functions.

Template 3: FastAPI + Python + SQLAlchemy

01-core.mdc (Always)

---
description: Project stack
alwaysApply: true
---

Python 3.12+, type hints everywhere. FastAPI + SQLAlchemy 2.0 async.
Package manager: Poetry. Testing: pytest + pytest-asyncio.

02-api.mdc (Auto-attach)

---
description: API and service layer conventions
globs: ["app/routers/**/*.py", "app/services/**/*.py"]
alwaysApply: false
---

- `async def` for all route handlers and service functions.
- Pydantic models for all request/response bodies — never return raw dicts.
- Business logic in `app/services/`, keep route handlers thin.
- DB queries in `app/repositories/` — no direct ORM queries in services.
- Plural nouns for resource paths: `/users`, not `/user`. Version prefix `/api/v1/`.

Template 4: Go + Gin + PostgreSQL

01-core.mdc (Always)

---
description: Project stack
alwaysApply: true
---

Go 1.22+. Gin web framework. `pgx` for PostgreSQL, no ORM.
Testing: standard `testing` package + `testify`.

02-backend.mdc (Auto-attach)

---
description: Handler, service, repository conventions
globs: "internal/**/*.go"
alwaysApply: false
---

- Errors always returned, never panicked on (except main.go init).
- `context.Context` as first parameter in all service and repository functions.
- Return `(T, error)` from service and repository functions.
- Handlers translate errors to HTTP status — services never set status codes.
- Every exported function has a godoc comment.

Template 5: Node.js + Express + TypeScript + PostgreSQL

01-core.mdc (Always)

---
description: Project stack
alwaysApply: true
---

Node.js 20+, TypeScript 5+, Express 4. PostgreSQL via `pg`, raw SQL — no ORM.
Testing: Jest + Supertest.

02-database.mdc (Auto-attach)

---
description: Database access rules
globs: "src/db/**/*.ts"
alwaysApply: false
---

- All SQL in `src/db/queries/` — never inline in services.
- Always use parameterized queries: `db.query('SELECT * FROM users WHERE id = $1', [id])`.
- Never concatenate user input into SQL strings.
- Wrap multi-step operations in transactions.

5. Migrating from .cursorrules to .mdc

You do not need to do this in one sitting. The recommended path:

  1. Create .cursor/rules/ and add one .mdc file — start with your most critical, universal rules as 01-core.mdc with alwaysApply: true.
  2. Split the rest by concern, not all at once — move sections from .cursorrules into scoped .mdc files as you touch that part of the codebase, verifying behavior each time.
  3. Keep .cursorrules in place during the transition — it still loads in Chat and Composer, so nothing breaks while you migrate.
  4. Test in Agent mode specifically — ask "what rules are you following?" in an Agent session. If it doesn't mention content that's still only in .cursorrules, that's expected — it confirms why the migration matters.
  5. Delete .cursorrules once everything is moved — having both creates undefined precedence behavior; see Cursor Rules Not Working for specifics.

6. Community Rule Collections

Useful starting points rather than copy-paste sources — treat community rules as raw material to adapt, not finished configuration:

  • awesome-cursorrules (PatrickJS) — the largest collection, organized by framework and language. Many entries still use the legacy single-file format; split them into scoped .mdc files rather than pasting in wholesale.
  • cursor.directory — a browsable, searchable directory of community rules by stack.
  • awesome-cursor-rules-mdc (sanjeed5) — community rules already converted to .mdc format with frontmatter, which saves the migration step.

Filter anything you copy for specificity: a rule like "Props interfaces are named ComponentNameProps" is worth keeping; "write clean, typed components" is not — if the model would do it anyway, the line just costs tokens.


7. Cursor Rules vs. Other Cursor Features

Mechanism What it is Persists Loads in Agent mode Best for
.cursor/rules/*.mdc Project-level, scoped instructions Yes Yes Architecture, stack conventions, naming, workflow rules
.cursorrules (legacy) Project-level, unscoped instructions Yes No Backward compatibility only — do not use for new projects
Global rules (Settings → Rules for AI) Personal developer defaults Yes Yes Personal preferences, agent behaviour, communication style
@file or @folder mentions Files referenced in a specific request No — per-request only Yes Task-specific context: a spec doc, a schema
In-request instructions Instructions given directly in the chat No — one request only Yes One-off overrides

Decision rule:

  • Will you need this instruction in more than one session? → .mdc rule, scoped appropriately
  • Personal preference that doesn't belong in the repo? → Global rules
  • Context specific to one task? → @file reference
  • One-time override? → Say it directly in the request

For the distinction between rules (how code should look) and system-prompt-level agent behavior (how the agent should act when uncertain), see System Prompts for AI Coding Agents.


8. Common Anti-Patterns

Anti-pattern Example Problem Better alternative
Starting a new project on .cursorrules Creating .cursorrules for a project that will use Agent mode Silently provides zero guidance in Agent mode Start with .mdc directly
Vague instruction "Write clean code." Cursor cannot operationalize "clean" "Functions must not exceed 40 lines. Max 3 levels of nesting."
Restating the obvious "Use good variable names." Wastes token budget Remove it
Overusing alwaysApply: true 10+ rules all marked always-apply Degrades attention on all of them; see token budget below Reserve Always for 3–5 genuinely universal rules
Both .cursorrules and .mdc present Migrating without removing the old file Undefined precedence between the two Complete the migration, then delete .cursorrules
Negative-only rules "Don't use Redux. Don't use class components." Cursor needs to know what to do, not just what to avoid "Use Zustand. Use functional components."

9. Token Budget and Rule Length

Every .mdc file that loads for a given request is included in that request's context. The current practical guidance:

  • Under 500 lines per individual .mdc file.
  • Combined alwaysApply: true content should stay under roughly 2,000–3,000 tokens total — beyond this, the model's attention on rules that load later in the combined content measurably degrades, causing it to skip constraints.

To stay within budget without losing coverage:

  • Keep alwaysApply: true reserved for genuinely universal, short rules — push everything else to Auto-attach or Agent-decided
  • Split by concern into multiple files rather than one long file
  • Reference existing code instead of repeating patterns: "Follow the structure of src/services/userService.ts"
  • Periodically remove rules that haven't fired in weeks — a rule with no recent effect is pure token cost

10. Frequently Asked Questions

Is .cursorrules completely dead?

No — it still loads in Chat and Composer for backward compatibility. But it is silently ignored in Agent mode, which is how most developers use Cursor for substantive work in 2026. Treat it as legacy: fine to leave in place temporarily during migration, not something to start a new project with.

Should I commit .cursor/rules/ to git?

Yes, in almost all cases. It encodes project conventions that every contributor (human and AI) should follow. Treat it like .eslintrc — part of your project's quality infrastructure.

What's the maximum size for a rule file?

No hard cap, but keep individual .mdc files under 500 lines and combined always-apply content under roughly 2,000–3,000 tokens for reliable adherence. Split large rule sets into focused, scoped files rather than one large one.

Do workspace rules override global Rules for AI?

Yes. Workspace .mdc rules take priority over global settings when they conflict. Global rules are your personal defaults; workspace rules are project-specific and win.

Can I write rules in a language other than English?

Yes. Cursor understands rules in any language the underlying model supports. English tends to produce the most consistent results for technical content.

How do I override a rule for a single task?

Give the override directly in your request: "For this task only, use a class component instead of a functional one." In-request instructions take precedence over persistent rules for that single interaction. See How to Prompt AI Coding Agents for more on structuring effective one-off task instructions.

My rules aren't working — where do I start debugging?

See the dedicated Cursor Rules Not Working troubleshooting guide, or the faster Why Cursor Ignores .cursorrules checklist for a quick diagnostic pass.


Related

Enjoyed this article?

Share it with your network