Cursor Rules
Cursor Rules are persistent instructions that live in your codebase and shape AI behavior for every interaction. How to write them, when to auto-attach them, and where to find good starting points.
Without rules, every AI interaction starts from zero. The AI doesn't know your preferred patterns, your naming conventions, or which libraries your project uses. You explain it in every prompt and it forgets the next session.
Cursor Rules fix this. They're persistent instructions — stored in your project or globally on your machine — that get included automatically in AI interactions.
Two Levels of Rules
Project Rules
Project-level rules live in your repository and apply to everyone on the team.
How to create them:
- Cursor Settings → Rules → "Add Rule"
- Or create a
.cursorrulesfile in your project root - Or use the newer format:
.cursor/rules/directory with individual.mdcfiles
What to put here:
- The tech stack with specific versions
- Your naming conventions
- Import patterns you enforce
- Patterns you want avoided
- Testing approach
Example project rules file:
You are working on a Next.js 15 TypeScript application.
Tech stack:
- Next.js 15.1 (App Router — not Pages Router)
- TypeScript 5.4 (strict mode enabled)
- Tailwind CSS for all styling
- Zustand for global state
- Tanstack Query for server state
- Vitest + React Testing Library for tests
- Zod for runtime validation
Conventions:
- Components: PascalCase, one per file, in src/components/
- Hooks: camelCase, prefixed with "use", in src/hooks/
- All imports use the "@/" alias (never relative "../")
- Use "import type" for type-only imports
- Server components by default; add "use client" only when needed
Must avoid:
- var (use const/let)
- any types (use unknown or proper types)
- Class components
- Inline styles (use Tailwind)
- console.log in committed code (use the logger at src/lib/logger.ts)
- default exports except for Next.js pages and layoutsThis file gets committed to git. Every team member, every AI interaction, inherits it.
User Rules (Global)
User-level rules apply to all projects on your machine. Good for personal preferences that span all work.
How to access: Cursor Settings → Rules → User Rules
Example user rules:
Keep explanations concise. Prefer code over prose.
When writing functions, use early returns instead of deeply nested conditions.
Always use explicit TypeScript return types on function declarations.
When I ask you to explain something, lead with a one-sentence summary,
then the detail.
Never add comments to code unless the logic isn't self-evident.These preferences get applied across every project, without repeating them per codebase.
Auto-Attach Rules
Regular rules are always included. Auto-attach rules are conditional — they activate only when specific file patterns are in context.
This keeps context lean. The AI only receives rules relevant to what it's currently working on.
How to create an auto-attach rule:
In Cursor's rules UI, specify the "Apply to" file glob pattern when creating a rule.
Example: testing conventions that only apply to test files:
Apply to: **/*.test.ts, **/*.spec.ts, **/*.test.tsx
When writing tests:
- Use describe() blocks to group related tests
- Prefer it() over test()
- One assertion per test where possible
- Mock at the module level with vi.mock(), not inside individual tests
- Use userEvent over fireEvent for interaction tests
- Always clean up timers and subscriptions with afterEachWhen you reference a .test.ts file in your conversation, this rule activates. When you're working on a component, it doesn't.
Other useful auto-attach patterns:
API routes: src/app/api/**/*.ts
Server components: src/app/**/page.tsx
Database layer: src/lib/db/**/*.ts, src/models/**/*.ts
Configuration files: *.config.ts, *.config.js
Migration files: migrations/**/*.sqlcursor.directory: Pre-Built Rules for Your Framework
Writing rules from scratch is slow. The community has already done much of this work.
cursor.directory is a community-maintained library of Cursor rules for common frameworks and use cases. Browse by framework — React, Next.js, Vue, Svelte, FastAPI, Django, and many more.
How to use it:
- Find a rule set that matches your tech stack
- Read it — don't just copy it blindly
- Edit it to match your specific conventions
- Add your "must avoid" patterns based on your project's history
A good framework rule is a starting point, not a final answer. The community rule for Next.js will get you 80% there; the remaining 20% is specific to your team's decisions.
What Makes a Good Rule
Good rules are:
- Specific: "Use Zod for all runtime validation at API boundaries"
- Actionable: "Import types separately with
import type" - Based on real patterns in your codebase
Bad rules are:
- Vague: "Write good code"
- Contradictory: conflicting rules cancel each other (and confuse the AI)
- Too long: the AI loses track of rules buried in a wall of text
Structure that works:
[Brief context: what this project is and who it's for]
Stack:
- [Technology choices with versions]
Conventions:
- [Naming patterns]
- [File structure rules]
- [Import patterns]
Must avoid:
- [Anti-patterns specific to this project]Keep it scannable. If you wouldn't read all of it, the AI won't either.
Debugging Rules That Aren't Working
If the AI keeps ignoring your rules:
-
Check for conflicts — do your project rules and user rules say opposite things? Conflicts cause the AI to pick one arbitrarily.
-
Is the rule too long? — trim it. Test which rules are actually being followed by asking: "What are the conventions for this project according to the rules you have?"
-
Check if it's loaded — in the Cursor chat panel, you can see which rules are active in the context panel at the bottom.
-
Be more explicit — "Import types with
import type" is clearer than "use proper TypeScript patterns."
The Compounding Return
Rules are the foundation of consistent AI behavior. An AI coding session without rules is like a new contractor who doesn't know how your team works. Rules are the onboarding documentation — written once, applied every time.
The return compounds: each good rule you write prevents the same mistake from appearing in every future session. A one-line rule — "Never use fetch directly, use the apiClient wrapper in src/lib/api.ts" — might save you from correcting the same thing for the next year.
Keep reading
Enjoyed this? Get more like it.
Deep dives on system design, React, web development, and personal finance — straight to your inbox. Free, always.