Cursor rules that actually work

Cursor reads two kinds of rule files: the legacy .cursorrules at the project root, and the newer per-feature .cursor/rules/*.mdc with front-matter. This guide explains both, what to put in them, and the common mistakes that cause Cursor to ignore your rules entirely.

The two formats

FormatPathScope
.cursorrulesProject rootWhole repo, always-on. Older, still supported.
.cursor/rules/*.mdc.cursor/rules/ folderPer-feature, scoped by glob via front-matter. Newer, recommended.
User-level .cursor/rules/*.mdc~/.cursor/rules/Applies across all your repos.

The .mdc format

---
description: Rules for the React frontend.
globs:
  - "src/**/*.tsx"
  - "src/**/*.ts"
alwaysApply: false
---

# React conventions

- Functional components only, hooks only.
- Tailwind for styling, no CSS modules.
- TanStack Query for server state, Zustand for client state.
- No useEffect for data fetching — use Query.

Three front-matter fields matter:

The common mistakes

  1. Writing 2000 words of prose. Cursor doesn't read essays — it reads rules. Use short, declarative bullets. "X, not Y" beats "we generally prefer X because…".
  2. No globs. Without globs, your rule never auto-loads. Set globs or use alwaysApply: true.
  3. Conflicting rules. Two .mdc files saying "use Zustand" and "use Jotai" → Cursor picks one and you blame the tool. Resolve conflicts at the project level.
  4. Generic "be a good engineer" rules. They don't change behavior. Be specific: "no useEffect for data fetching", not "write clean code".
  5. Forgetting to commit. Rules in .cursor/rules/ belong in git. Teammates need them too.

A working .cursorrules template

# Project rules

## Stack
- Frontend: React 19 + Vite 8 + TypeScript 5 + Tailwind 4 + shadcn/ui
- Backend: Fastify + Prisma + Postgres
- Tests: Vitest (unit), Playwright (e2e)
- Package manager: pnpm

## Workflow
- Plan before code. Write PLAN.md when scope > one file.
- Failing test first. No green, no commit.
- One feature per commit. Conventional commits.
- 3 retries then ask. Don't grind forever.
- Quality gate: lint + types + tests + build all green = "done".

## Style
- Functional components only. Hooks only.
- Tailwind utility classes. No CSS modules, no styled-components.
- Server state with TanStack Query. Client state with Zustand.
- No useEffect for data fetching.
- No emojis in code or output.
- Latest stable library versions.

## Banned
- Defensive over-engineering. No try/except wrappers without a reason.
- Workarounds. Find root cause, then fix.
- Mega-PRs. One feature per commit.
- Secrets in code. Use .env / .env.example.

Keep it in sync with your other agents

If you use Cursor and Claude Code and Copilot, you don't want three drift-prone copies. Generate them all from one template instead:

myVibe ships a per-project initializer that creates .cursorrules, AGENTS.md, CLAUDE.md, .windsurfrules, and .github/copilot-instructions.md from one template, so every agent reads the same rules.

pwsh -File ~/.agents/skills/myvibe/myvibe-init.ps1   # Windows
bash ~/.agents/skills/myvibe/myvibe-init.sh          # macOS / Linux

One-line install

# Windows
iwr https://raw.githubusercontent.com/Mohamed201389/myVibe/main/bootstrap.ps1 | iex

# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/Mohamed201389/myVibe/main/bootstrap.sh | bash
View on GitHub → Back to overview

Further reading