Custom Rules & Project Configuration

45 min intermediate Lesson 6

Learning Outcomes

  • Understand what .cursor/rules does and where it lives in your project
  • Write effective rules that shape AI-generated code
  • Create project-specific instructions for framework conventions and naming patterns
  • Share rules across team members via version control
  • Evolve rules over time as your project grows and patterns change

Lesson Plan

Segment Duration Topic
Intro 4 min Why default AI output needs project-specific guidance
Demo 1 8 min Creating your first rule files
Demo 2 8 min Writing rules for code style and framework conventions
Explain 5 min How rules interact with context and prompts
Demo 3 8 min Team sharing and version control strategies
Demo 4 7 min Evolving rules — adding, refining, and removing over time
Wrap-up 5 min Best practices and key takeaways

Before You Begin

Pre-work:

  • Complete Lesson 5 — Debugging with Cursor
  • Have a project with established coding conventions (naming, file structure, patterns)
  • Identify 3-5 conventions your team follows that AI might not know about

Shopping List:

  • A project open in Cursor (preferably one with a style guide or established patterns)
  • Access to create files in the project root directory
  • Examples of "good" code in your project that follows your conventions

1 What Cursor Rules Are and Where They Live

Cursor rules are .mdc files in the .cursor/rules/ directory of your project. When Cursor's AI generates code, it reads these rules and follows the instructions inside them — treating them as persistent context for every interaction.

Where they live:

your-project/
├── .cursor/
│   └── rules/
│       ├── general.mdc       ← project-wide conventions
│       ├── typescript.mdc    ← TypeScript-specific rules
│       └── testing.mdc       ← testing conventions
├── .gitignore
├── package.json
├── src/
│   └── ...

What they do:

  • Provide persistent instructions to the AI model across all interactions (Ask, Cmd+K, Agent)
  • Act like a system prompt that's always active when you're working in this project
  • Override default AI behaviour with your specific preferences
  • Apply to everyone who opens the project in Cursor (if committed to version control)

How they differ from one-off prompts:

Approach Persistence Scope Use Case
Ask/Agent message One conversation Current chat Ad-hoc questions
Cmd+K prompt One generation Current edit Specific code generation
.cursor/rules/*.mdc Always active Entire project Team conventions
AGENTS.md Always active Entire project Alternative (plain markdown)

Creating your first rule file:

  1. Open the terminal in Cursor with Ctrl+`
  2. Run: mkdir -p .cursor/rules && touch .cursor/rules/general.mdc
  3. Open the file and add your rules
  1. Open the terminal in Cursor with Ctrl+`
  2. Run: mkdir -p .cursor/rules && echo. > .cursor/rules/general.mdc
  3. Open the file and add your rules

Rule file format (.mdc):

---
description: General project conventions
globs: "**/*"
alwaysApply: true
---

# Project Conventions

- Use TypeScript strict mode
- Follow camelCase for variables
- All functions must have JSDoc comments

The frontmatter controls when the rule applies:

  • description — what this rule is for (shown in settings UI)
  • globs — which files this rule applies to (e.g., "src/**/*.ts")
  • alwaysApply: true — include in every interaction regardless of file context
NOTE
How It Works
Cursor reads .cursor/rules/*.mdc files at the start of every AI interaction. Rules with matching globs are included based on the files you're working with. Rules with alwaysApply: true are always included.
TIP
Tip
Cursor also supports rules configured in Settings > Cursor Settings > Rules, and plain AGENTS.md files at the project root. The .cursor/rules/ directory is the recommended approach for version-controlled, scoped rules.

2 Writing Effective Rules for Code Generation

Good rules are specific, actionable, and concise. The AI model responds best to clear instructions that describe what you want — not vague aspirations.

Rule writing principles:

  1. Be specific — "Use camelCase for variables" beats "Use good naming"
  2. Be positive — "Always use functional components" beats "Don't use class components"
  3. Give examples — Show the pattern you want, not just describe it
  4. Stay concise — The model has limited context; don't waste it on filler
  5. Prioritise — Put the most important rules first

Example: A basic rule files

# Project Conventions

## Language & Framework
- This is a Next.js 14 project using the App Router
- Use TypeScript for all new files
- Use Server Components by default; add "use client" only when needed

## Code Style
- Use named exports, not default exports
- Use arrow functions for components: export const MyComponent = () => {}
- Prefer const over let; never use var
- Use early returns to reduce nesting

## Naming
- Components: PascalCase (e.g., UserProfile)
- Files: kebab-case (e.g., user-profile.tsx)
- Hooks: camelCase with "use" prefix (e.g., useUserData)
- Types/Interfaces: PascalCase with no prefix (e.g., User, not IUser)

## Structure
- Place components in src/components/
- Place utilities in src/lib/
- Place API routes in src/app/api/
- Co-locate tests with source files (Component.test.tsx next to Component.tsx)

What makes rules effective — before and after:

Weak Rule Strong Rule
"Write good code" "All functions must have TypeScript return types"
"Handle errors properly" "Wrap async operations in try/catch and log errors with console.error"
"Use modern JavaScript" "Use ES2022+ features: optional chaining, nullish coalescing, array.at()"
"Follow best practices" "Extract reusable logic into custom hooks under src/hooks/"

Testing your rules:

After writing rules, test them:

  1. Open Chat and ask: "Create a new React component for a user profile card"
  2. Check if the output matches your rules (naming, exports, structure)
  3. If not, refine the rules and try again
WARNING
Watch Out
Don't put sensitive information in .cursor/rules (API keys, internal URLs, credentials). This file should be committed to version control and will be visible to anyone with repo access.

3 Project-Specific Instructions — Framework Conventions

Different projects have different patterns. Your rules should reflect the specific decisions your team has made — not generic best practices.

Framework-specific rules:

For a React + Zustand project:

## State Management
- Use Zustand for global state (not Redux, not Context for shared state)
- Create stores in src/stores/ with the pattern: useXxxStore.ts
- Use selectors to avoid unnecessary re-renders:
  const count = useCountStore((state) => state.count)
- Never access the store outside of React components — use store.getState() only in tests

For a Python FastAPI project:

## API Design
- Use Pydantic v2 models for all request/response bodies
- Place route handlers in src/routes/ grouped by resource
- Use dependency injection for database sessions
- Return proper HTTP status codes: 201 for creation, 204 for deletion
- All endpoints must have docstrings (shown in OpenAPI docs)

## Error Handling
- Raise HTTPException with detail messages, never return raw errors
- Use custom exception handlers in src/exceptions.py
- Log errors with structlog, not print() or logging module directly

For a Vue 3 + Composition API project:

## Vue Conventions
- Use <script setup lang="ts"> for all SFCs
- Use composables (useXxx) for reusable logic, placed in src/composables/
- Use defineProps with TypeScript types, not runtime validation
- Prefer computed() over watch() where possible
- Use Pinia stores in src/stores/, one store per domain concept

Architecture rules:

Beyond code style, encode your architecture decisions:

## Architecture Rules
- Components should not make API calls directly
- All data fetching goes through React Query hooks in src/hooks/queries/
- Domain logic lives in src/services/ — never in components
- Components are pure view layer: receive data via props, emit events up
- Maximum component file length: 150 lines. If longer, decompose.

Dependency rules:

## Dependencies
- Do not add new npm packages without team discussion
- Prefer native browser APIs over library solutions (e.g., fetch over axios)
- Use date-fns for date manipulation (not moment, not dayjs)
- For icons, use Lucide React — do not import from other icon libraries
TIP
Tip
Look at your PR review comments from the past month. Every repeated comment ('we don't do it that way', 'please use X instead of Y') is a candidate for a rule entry. This is the fastest way to build effective rules.
NOTE
How It Works
The AI uses these rules as constraints when generating code. It's similar to giving a new team member a style guide — they'll follow it most of the time, but may occasionally need a reminder. If the AI ignores a rule, reinforce it in your prompt: 'Remember to use named exports as specified in our project rules.'

4 Sharing Rules Across Team Members

One of the most powerful aspects of .cursor/rules is team alignment. When everyone uses the same rules, AI-generated code is consistent regardless of who generates it.

Version control strategy:

The rule files should be committed to your repository:

git add .cursor/rules/
git commit -m "Add AI coding rules for team consistency"

What to include in .gitignore (personal preferences):

Some teams create a personal rules file that's git-ignored:

# .gitignore
.cursor/rules.local    # Personal preferences not shared with team

Then reference both: the shared .cursor/rules applies to everyone, while .cursor/rules.local (if Cursor supports it) or Cursor Settings > Rules for AI handles personal preferences.

Team adoption workflow:

  1. Start small — Begin with 5-10 rules that the team already agrees on
  2. Review in PR — Treat rule file changes like code changes; discuss in PRs
  3. Document reasoning — Add comments explaining why a rule exists:
# We use named exports because they provide better IDE refactoring
# support and prevent ambiguous default import names.
- Use named exports, not default exports
  1. Iterate together — Schedule a monthly review of the rules file

Onboarding benefit:

When a new developer joins the team:

  • They clone the repo
  • The rule files is already there
  • Their AI immediately generates code that follows team conventions
  • Fewer PR review comments about style and patterns

Rules for different environments:

If your monorepo has multiple apps with different conventions:

your-monorepo/
├── .cursor/rules/             ← shared rules for entire repo
│   └── general.mdc
├── apps/
│   ├── web/
│   │   └── .cursor/rules/    ← web-specific rules
│   │       └── web.mdc
│   └── mobile/
│       └── .cursor/rules/    ← mobile-specific rules
│           └── mobile.mdc
└── packages/
    └── shared/
        └── .cursor/rules/    ← shared library rules
            └── library.mdc

Cursor uses rules from the .cursor/rules/ directory closest to the file you're editing. If you're working in apps/web/src/, it reads rules from apps/web/.cursor/rules/. Nested directories are supported.

WARNING
Watch Out
Avoid rules that are too opinionated about things the team hasn't agreed on. A rule files should represent consensus, not one person's preference. If there's disagreement, leave the rule out until the team aligns.
TIP
Tip
Include a header comment in your rule files with the last review date and who maintains it. This helps the team know if rules are current: '# Last reviewed: 2024-12-01 | Maintainer: @teamlead'

5 Evolving Rules Over Time

Your rule files is a living document. As your project grows, your rules should grow with it — but also be pruned when they're no longer relevant.

When to add new rules:

  • You notice the AI repeatedly generating code in a pattern you don't want
  • The team adopts a new library or convention
  • A bug was caused by a pattern you want to prevent
  • New team members keep getting the same PR feedback

When to remove rules:

  • A rule references a library you've migrated away from
  • A rule is so obvious the AI always follows it without being told
  • A rule conflicts with a newer decision
  • The file is getting too long (aim for under 100 lines)

Evolving a rules file — real example:

Month 1 (project start):

- Use TypeScript
- Use React functional components
- Use CSS modules for styling

Month 3 (patterns established):

- Use TypeScript with strict mode
- Use React functional components with arrow function syntax
- Use CSS modules with camelCase class names
- Place shared components in src/components/common/
- Use React Query for all server state
- Use Zustand for client state

Month 6 (team growing, conventions mature):

## TypeScript
- Strict mode enabled; never use 'any' — use 'unknown' and narrow
- Define API response types in src/types/api/
- Use discriminated unions for state machines

## Components
- Arrow function components with named exports
- Props interface defined above component: interface XxxProps {}
- Maximum 150 lines per component file
- Decompose into sub-components in a folder: Component/index.tsx, Component/Header.tsx

## Data Fetching
- React Query for all server state; queries in src/hooks/queries/
- Mutations in src/hooks/mutations/
- Optimistic updates for user-facing actions
- Error boundaries at route level

## Testing
- Co-locate test files: Component.test.tsx
- Use React Testing Library; test behaviour, not implementation
- Mock API calls with MSW, not jest.mock

Measuring effectiveness:

Track these signals to know if your rules are working:

  • Fewer style-related PR comments over time
  • New team members ramp up faster
  • AI output requires less manual correction
  • Generated code passes linting on the first try

Maintaining rule quality:

# GOOD: Specific, testable, actionable
- Error messages must be user-friendly strings, not raw Error objects
- All API endpoints must validate input with zod schemas
- Database queries must use parameterized values, never string interpolation

# BAD: Vague, subjective, unverifiable
- Write clean code
- Follow best practices
- Keep things simple
TIP
Tip
Create a 'rules changelog' comment at the bottom of your rule files. When you add or remove a rule, note why. This helps future you (and teammates) understand the rationale: '# 2024-11: Added MSW rule after mocking inconsistency caused flaky tests'
NOTE
How It Works
There is a practical limit to .cursor/rules length. If the file exceeds about 2000-3000 tokens, the AI may pay less attention to rules at the end. Keep rules concise and prioritised — put the most important conventions first.

6 Advanced Rule Patterns

Once you're comfortable with basic rules, these advanced patterns help you get even more precise output from the AI.

Pattern 1: Show, don't just tell

Include a short code example in your rules:

## Component Pattern
Every component should follow this structure:

interface ComponentProps {
  // props here
}

export const Component = ({ prop1, prop2 }: ComponentProps) => {
  // hooks first
  // derived state
  // handlers
  // early returns for loading/error
  // main return
}

Pattern 2: Conditional rules

## Conditional Conventions
- For files in src/pages/: Use default exports (Next.js requirement)
- For files everywhere else: Use named exports
- For test files: Use describe/it blocks, not test()
- For utility functions: Always include JSDoc with @param and @returns

Pattern 3: Anti-patterns (what NOT to generate)

## Anti-Patterns — Do Not Generate
- No useEffect for data fetching (use React Query instead)
- No prop drilling more than 2 levels (use composition or context)
- No inline styles (use CSS modules or Tailwind classes)
- No console.log in committed code (use our logger utility)
- No relative imports going up more than 2 levels (use path aliases @/)

Pattern 4: Technology constraints

## Technology Decisions (Do Not Override)
- Database: PostgreSQL via Prisma ORM (not raw SQL, not Knex, not TypeORM)
- Auth: NextAuth.js with JWT strategy (not custom auth, not session-based)
- Deployment: Vercel (do not suggest Docker or AWS-specific solutions)
- Node version: 20 LTS (do not use features unavailable in Node 20)

Pattern 5: Documentation rules

## Documentation
- Public functions must have JSDoc comments
- Complex algorithms must have a comment explaining the approach
- TODO comments must include a ticket number: // TODO(PROJ-123): description
- Do not generate comments that merely restate the code

Combining .cursor/rules with @-mentions:

Your rules set the baseline; @-mentions add context for specific requests:

  • .cursor/rules says: "Use React Query for server state"
  • Your prompt says: "Create a hook to fetch user data @src/types/user.ts @src/lib/api.ts"
  • The AI follows the rules AND uses the referenced files for types and API patterns
TIP
Tip
Start a rule files for each new project on day one — even if it only has 3-4 rules. It's much easier to add rules incrementally than to retrofit them after thousands of lines of inconsistent AI-generated code.
WARNING
Watch Out
Rules that are too restrictive can slow down the AI. If you find yourself fighting the AI's suggestions constantly, your rules might be too granular. Focus on patterns and conventions, not micro-managing every line.

Questions & Answers

Q: Does .cursor/rules affect all AI features equally (Chat, Cmd+K, Agent)?
Yes. The .cursor/rules content is included in the context for all AI interactions within the project. Whether you're using inline edit (Cmd+K), the AI side pane, or Agent mode, the rules are active. However, in very short interactions (like a quick Cmd+K edit), the AI may focus more on your immediate instruction than on peripheral rules.
Q: What happens if my .cursor/rules conflicts with what I ask in a prompt?
Your direct prompt typically wins. If your rules say "use CSS modules" but you ask "style this with Tailwind classes", the AI will use Tailwind for that specific request. Rules are defaults and guidelines — explicit instructions override them. This is by design, as you sometimes need to break your own rules for valid reasons.
Q: Is there a file size limit for .cursor/rules?
There's no hard file size limit, but there's a practical one. The rules consume context window tokens alongside your code and prompts. A file over 2000-3000 tokens may result in lower attention to later rules. Keep it focused: 50-100 lines covering your most important conventions. If you need more, consider splitting into a linked document referenced by a brief summary in .cursor/rules.
Q: Can I have different rules for different parts of a monorepo?
Yes. Place a rule files in any subdirectory, and Cursor will use the one closest to the file being edited. For a monorepo with apps/web and apps/api, you can have web-specific frontend rules and API-specific backend rules. A root-level .cursor/rules provides shared rules for the entire codebase.

Key Takeaways

  1. .cursor/rules lives in project root and provides persistent AI instructions for every interaction
  2. Be specific and actionable — "Use named exports" is better than "Follow best practices"
  3. Include examples — showing the pattern you want is more effective than describing it
  4. Commit to version control — team alignment is one of the biggest benefits
  5. Evolve over time — add rules when you see repeated issues; remove rules that are no longer relevant
  6. Keep it concise — aim for under 100 lines; prioritise the most important rules first

Next Steps: In Lesson 7 — Agent Mode, you'll learn how to use Cursor's autonomous Agent mode to complete complex multi-step tasks with minimal intervention.