How to write Cursor rules that actually work
Start with correct frontmatter
Every .mdc file needs a YAML frontmatter block. Without it, Cursor may silently ignore the entire file.
---
description: TypeScript error handling for API routes
globs:
- "src/api/**/*.ts"
- "src/routes/**/*.ts"
---
Three fields matter:
- description (required): Tells Cursor when to apply this rule. Be specific. "TypeScript error handling for API routes" beats "error handling."
- alwaysApply: Set to
truefor rules that should fire on every request. Use sparingly. - globs: File patterns that scope the rule. Rules with globs only load when you're editing matching files. This saves tokens.
Common mistake: Setting alwaysApply: false with no globs. This creates a rule that only fires when manually referenced by name. If that's not what you want, add globs or set alwaysApply: true.
Be specific, not vague
The model already knows how to "write clean code." Telling it to do so wastes tokens and changes nothing.
❌ Vague
Write clean code.
Follow best practices.
Handle errors properly.
Use good naming.
✓ Specific
Wrap all route handlers in try-catch.
Return {error, code} on failure.
Prefix private methods with underscore.
Name booleans with is/has/should.
Show, don't tell
A code example is worth 50 tokens of explanation. The model follows patterns better than prose.
❌ Telling
When handling errors in API routes, make sure to catch all errors and return a structured JSON response with an error message and an appropriate HTTP status code.
✓ Showing
catch (error) {
return Response.json(
{ error: error.message,
code: "INTERNAL_ERROR" },
{ status: 500 }
);
}
Use imperative language
Rules are commands, not suggestions. The model follows direct instructions better than hedged requests.
- Don't: "You might want to consider using const instead of let"
- Do: "Use const for all variables that aren't reassigned"
- Don't: "Please try to avoid nested ternaries"
- Do: "Never nest ternary operators. Use if/else instead."
Skip "please," "try to," "consider," and "maybe." These waste tokens and weaken the instruction.
One concern per rule
A rule that covers TypeScript naming, React component structure, AND testing patterns is too broad. The model handles focused rules better.
Split by concern:
typescript-naming.mdc— variable, function, and type naming conventionsreact-components.mdc— component structure, props, hookstesting-patterns.mdc— test structure, mocking, assertions
Say what to do, not just what to avoid
"Don't use var" tells the model what's wrong but not what's right. Always provide the alternative.
❌ Negation only
Don't use var.
Don't use any.
Avoid nested callbacks.
✓ With alternative
Use const instead of var. Use let only for reassignment.
Use specific types instead of any. Use unknown for truly unknown types.
Use async/await instead of nested callbacks.
Validate before committing
Run your rules through the linter to catch broken frontmatter, vague instructions, and other issues before they hit your project:
npx cursor-doctor scan
Or paste a single rule into the playground for instant feedback.
Related guides
- Cursor token budget: how many rules is too many?
- How to fix Cursor rule conflicts
- Generate Cursor rules from your codebase with AI
Try the playground
Paste a rule and get instant feedback on frontmatter, structure, and prompt engineering.
Open Playground →