---
description: Meta-rule for creating effective Cursor IDE rules with best practices, patterns, and examples
tags: [meta, cursor, documentation, best-practices]
---

# Creating Cursor Rules - Meta Rule

## Overview

This is a meta-rule for creating effective `.cursor/rules` files. Apply these principles when writing or improving Cursor IDE rules for your project.

## When to Use

**Use when:**
- Starting a new project and setting up `.cursor/rules`
- Improving existing project rules
- Converting skills or guidelines to Cursor format
- Team needs consistent coding standards

**Don't use for:**
- Claude Code skills (those go in `.claude/skills/`)
- One-time instructions (just ask directly)
- User-specific preferences (those go in global settings)

## Core Principles

### 1. Be Specific and Actionable

```markdown
# ❌ BAD - Vague
Write clean code with good practices.

# ✅ GOOD - Specific
Use functional components with TypeScript.
Define prop types with interfaces, not inline types.
Extract hooks when logic exceeds 10 lines.
```

### 2. Focus on Decisions, Not Basics

```markdown
# ❌ BAD - Obvious
Use semicolons in JavaScript.
Indent with 2 spaces.

# ✅ GOOD - Decision guidance
Choose Zustand for global state, React Context for component trees.
Use Zod for runtime validation at API boundaries only.
Prefer server components except for: forms, client-only APIs, animations.
```

### 3. Organize by Concern

```markdown
# ✅ GOOD Structure

## Tech Stack
- Next.js 14 with App Router
- TypeScript strict mode
- Tailwind CSS for styling

## Code Style
- Functional components only
- Named exports (no default exports)
- Co-locate tests with source files

## Patterns
- Use React Server Components by default
- Client components: mark with "use client" directive
- Error handling: try/catch + toast notification

## Project Conventions
- API routes in app/api/
- Components in components/ (flat structure)
- Types in types/ (shared), components/*/types.ts (local)
```

## Rule Anatomy

### MDC Format and Metadata

Cursor rules are written in **MDC (.mdc)** format, which supports YAML frontmatter metadata and markdown content. The metadata controls how and when rules are applied.

### Required YAML Frontmatter

Every Cursor rule MUST start with YAML frontmatter between `---` markers:

```yaml
---
description: Brief description of when and how to use this rule
globs: ["**/*.ts", "**/*.tsx"]
alwaysApply: false
---
```

### Frontmatter Properties

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `description` | string | **Yes** | Brief description of the rule's purpose. Used by AI to decide relevance. Never use placeholders like `---` or empty strings. |
| `globs` | array | No | File patterns that trigger auto-attachment (e.g., `["**/*.ts"]`). Leave empty or omit if not using Auto Attached type. |
| `alwaysApply` | boolean | No | If `true`, rule is always included in context. If `false` or omitted, behavior depends on Rule Type. |

### Rule Types

Control how rules are applied using the **type dropdown** in Cursor:

| Rule Type | Description | When to Use |
|-----------|-------------|-------------|
| **Always** | Always included in model context | Core project conventions, tech stack, universal patterns that apply everywhere |
| **Auto Attached** | Included when files matching `globs` pattern are referenced | File-type specific rules (e.g., React components, API routes, test files) |
| **Agent Requested** | Available to AI, which decides whether to include it based on `description` | Contextual patterns, specialized workflows, optional conventions |
| **Manual** | Only included when explicitly mentioned using `@ruleName` | Rarely-used patterns, experimental conventions, legacy documentation |

### Examples by Rule Type

**Always Rule** (Core conventions):
```yaml
---
description: TypeScript and code style conventions for the entire project
alwaysApply: true
---
```

**Auto Attached Rule** (File pattern-specific):
```yaml
---
description: React component patterns and conventions
globs: ["**/components/**/*.tsx", "**/app/**/*.tsx"]
alwaysApply: false
---
```

**Agent Requested Rule** (Contextual):
```yaml
---
description: RPC service boilerplate and patterns for creating new RPC endpoints
globs: []
alwaysApply: false
---
```

**Manual Rule** (Explicit invocation):
```yaml
---
description: Legacy API migration patterns (deprecated, use for reference only)
globs: []
alwaysApply: false
---
```

### Best Practices for Frontmatter

1. **Description is mandatory** - AI uses this to determine relevance. Be specific:
   - ❌ Bad: `Backend code`
   - ✅ Good: `Fastify API route patterns, error handling, and validation using Zod`

2. **Use globs strategically** - Auto-attach to relevant file types:
   - React components: `["**/*.tsx", "**/*.jsx"]`
   - API routes: `["**/api/**/*.ts", "**/routes/**/*.ts"]`
   - Tests: `["**/*.test.ts", "**/*.spec.ts"]`

3. **Avoid always applying everything** - Use `alwaysApply: true` sparingly:
   - ✅ Good for: Tech stack, core conventions, project structure
   - ❌ Bad for: Framework-specific patterns, specialized workflows

4. **Make Agent Requested rules discoverable** - Write descriptions that help AI understand when to use:
   - Include keywords: "boilerplate", "template", "pattern for X"
   - Mention specific use cases: "when creating new API routes"

### Additional Optional Fields

While the core properties above control rule behavior, you may also include:

**title:** (Optional)
- Clear, concise name for the rule
- Example: `Creating Cursor Rules`, `TypeScript Type Safety`

**tags:** (Optional)
- Array of relevant tags for organization
- Use lowercase, kebab-case
- Example: `[meta, cursor, documentation, best-practices]`

**source:** (Optional)
- Where the rule originated from
- Example: `claude-code-skill`, `custom`, `community`

**IMPORTANT:** The `description` field is MANDATORY for all cursor rules. When converting skills to cursor rules or creating new rules, always include a valid description. Never use placeholders like `---` or empty strings.

## Required Sections

### Tech Stack Declaration

```markdown
## Tech Stack
- Framework: Next.js 14
- Language: TypeScript 5.x (strict mode)
- Styling: Tailwind CSS 3.x
- State: Zustand
- Database: PostgreSQL + Prisma
- Testing: Vitest + Playwright
```

**Why:** Prevents AI from suggesting wrong tools/patterns.

### Code Style Guidelines

```markdown
## Code Style
- **Components**: Functional with TypeScript
- **Props**: Interface definitions, destructure in params
- **Hooks**: Extract when logic > 10 lines
- **Exports**: Named exports only (no default)
- **File naming**: kebab-case.tsx
```

### Common Patterns

```markdown
## Patterns

### Error Handling
```typescript
try {
  const result = await operation();
  toast.success('Operation completed');
  return result;
} catch (error) {
  const message = error instanceof Error ? error.message : 'Unknown error';
  toast.error(message);
  throw error; // Re-throw for caller to handle
}
```

### API Route Structure
```typescript
// app/api/users/route.ts
export async function GET(request: Request) {
  try {
    // 1. Parse/validate input
    // 2. Check auth/permissions
    // 3. Perform operation
    // 4. Return Response
  } catch (error) {
    return new Response(JSON.stringify({ error: 'Message' }), {
      status: 500
    });
  }
}
```
```

### What NOT to Include

```markdown
# ❌ AVOID - Too obvious
- Write readable code
- Use meaningful variable names
- Add comments when necessary
- Follow best practices

# ❌ AVOID - Too restrictive
- Never use any third-party libraries
- Always write everything from scratch
- Every function must be under 5 lines

# ❌ AVOID - Language-agnostic advice
- Use design patterns
- Think before you code
- Test your code
```

## Structure Template

```markdown
# Project Name - Cursor Rules

## Tech Stack
[List all major technologies]

## Code Style
[Specific style decisions]

## Project Structure
[Directory organization]

## Patterns
[Common patterns with code examples]

### Pattern Name
[Description]
```code example```

## Conventions
[Project-specific conventions]

## Common Tasks
[Frequent operations with snippets]

### Task Name
```
step 1
step 2
```

## Anti-Patterns
[What to avoid and why]

## Testing
[Testing approach and patterns]
```

## Example Sections

### Tech Stack Section

```markdown
## Tech Stack

**Framework:** Next.js 14 (App Router)
**Language:** TypeScript 5.x (strict mode enabled)
**Styling:** Tailwind CSS 3.x with custom design system
**State:** Zustand for global, React Context for component trees
**Forms:** React Hook Form + Zod validation
**Database:** PostgreSQL with Prisma ORM
**Testing:** Vitest (unit), Playwright (E2E)
**Deployment:** Vercel

**Key Dependencies:**
- `@tanstack/react-query` for server state
- `date-fns` for date manipulation (not moment.js)
- `clsx` + `tailwind-merge` for conditional classes
```

### Patterns Section with Code

```markdown
## Patterns

### Server Component Data Fetching

```typescript
// app/users/page.tsx
import { prisma } from '@/lib/prisma';

export default async function UsersPage() {
  // Fetch directly in server component
  const users = await prisma.user.findMany({
    select: {
      id: true,
      name: true,
      email: true
    }
  });

  return <UserList users={users} />;
}
```

### Client Component with State

```typescript
'use client';

import { useState } from 'react';
import { toast } from 'sonner';

interface FormProps {
  onSubmit: (data: FormData) => Promise<void>;
}

export function Form({ onSubmit }: FormProps) {
  const [loading, setLoading] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);

    try {
      await onSubmit(new FormData(e.target as HTMLFormElement));
      toast.success('Saved successfully');
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to save';
      toast.error(message);
    } finally {
      setLoading(false);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* form fields */}
      <button disabled={loading}>
        {loading ? 'Saving...' : 'Save'}
      </button>
    </form>
  );
}
```
```

### Anti-Patterns Section

```markdown
## Anti-Patterns

### ❌ Don't: Default Exports
```typescript
// ❌ BAD
export default function Button() { }

// ✅ GOOD
export function Button() { }
```

**Why:** Named exports are more refactor-friendly and enable better tree-shaking.

### ❌ Don't: Inline Type Definitions
```typescript
// ❌ BAD
function UserCard({ user }: { user: { name: string; email: string } }) { }

// ✅ GOOD
interface User {
  name: string;
  email: string;
}

function UserCard({ user }: { user: User }) { }
```

**Why:** Reusability and discoverability.

### ❌ Don't: Client Components for Static Content
```typescript
// ❌ BAD
'use client';
export function StaticContent() {
  return <div>Static text</div>;
}

// ✅ GOOD - Server component by default
export function StaticContent() {
  return <div>Static text</div>;
}
```

**Why:** Server components are faster and reduce bundle size.
```

## Common Tasks

Include shortcuts for frequent operations:

```markdown
## Common Tasks

### Adding a New API Route

1. Create `app/api/[route]/route.ts`
2. Define HTTP method exports (GET, POST, etc.)
3. Validate input with Zod schema
4. Use try/catch for error handling
5. Return `Response` object

```typescript
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(1)
});

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const data = schema.parse(body);

    // Process...

    return Response.json({ success: true });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return Response.json(
        { error: error.errors },
        { status: 400 }
      );
    }
    return Response.json(
      { error: 'Internal error' },
      { status: 500 }
    );
  }
}
```

### Adding a New Component

1. Create `components/component-name.tsx`
2. Define props interface
3. Export as named export
4. Co-locate test if complex logic

```typescript
// components/user-card.tsx
interface UserCardProps {
  name: string;
  email: string;
  onEdit?: () => void;
}

export function UserCard({ name, email, onEdit }: UserCardProps) {
  return (
    <div className="rounded-lg border p-4">
      <h3 className="font-semibold">{name}</h3>
      <p className="text-sm text-gray-600">{email}</p>
      {onEdit && (
        <button onClick={onEdit}>Edit</button>
      )}
    </div>
  );
}
```
```

## Best Practices

### Keep Rules Under 500 Lines

- Split large rules into multiple, composable files
- Each rule file should focus on one domain or concern
- Reference other rule files when needed (e.g., "See `backend-api.mdc` for API patterns")
- **Why:** Large files become unmanageable and harder for AI to process effectively

### Split Into Composable Rules

Break down by concern rather than creating one monolithic file:

```
.cursor/rules/
  ├── tech-stack.mdc          # Core technologies
  ├── typescript-patterns.mdc # Language-specific patterns
  ├── api-conventions.mdc     # API route standards
  ├── component-patterns.mdc  # React/UI patterns
  └── testing-standards.mdc   # Testing approaches
```

**Why:** Easier to maintain, update, and reuse across similar projects.

### Provide Concrete Examples or Referenced Files

Instead of vague guidance, always include:
- Complete, runnable code examples
- References to actual project files: `See components/auth/LoginForm.tsx for example`
- Links to internal docs or design system
- Specific file paths and line numbers when relevant

**❌ BAD - Vague:**
```markdown
Use proper error handling in API routes.
```

**✅ GOOD - Concrete:**
```markdown
API routes must use try/catch with typed errors. Example:
```typescript
// app/api/users/route.ts (lines 10-25)
export async function POST(request: Request) {
  try {
    const data = await request.json();
    return Response.json({ success: true });
  } catch (error) {
    return handleApiError(error); // See lib/errors.ts
  }
}
```
See `app/api/products/route.ts` for complete implementation.
```

### Avoid Vague Guidance - Write Rules Like Clear Internal Docs

Rules should read like technical documentation, not casual advice:
- Be precise and unambiguous
- Include the "why" behind decisions
- Document exceptions to rules
- Reference architecture decisions
- Link to related rules or documentation

**Think:** "Could a new engineer understand this without asking questions?"

### Reuse Rules When Repeating Prompts in Chat

If you find yourself giving the same instructions repeatedly in chat:
1. Document that pattern in `.cursor/rules/`
2. Include the specific guidance you keep repeating
3. Add examples of correct implementation
4. Update existing rule files rather than creating new ones

**Common scenarios to capture:**
- "Always use X pattern for Y"
- "Don't forget to Z when doing W"
- Corrections you make frequently
- Patterns specific to your team/codebase

### Keep It Scannable

- Use headers and sections
- Bold important terms
- Code examples for clarity
- Tables for comparisons
- Add table of contents for files over 200 lines

### Update Regularly

- Review monthly or after major changes
- Remove outdated patterns
- Add new patterns as they emerge
- Keep examples current
- Archive deprecated rules rather than deleting (for reference)

### Test with AI

Ask AI to:
1. "Create a new API route following our conventions"
2. "Add error handling to this component"
3. "Refactor this to match our patterns"

Verify it follows your rules correctly.

## Real-World Example

See the PRPM registry `.cursor/rules` for a complete example:
- Clear tech stack declaration
- Specific TypeScript patterns
- Fastify-specific conventions
- Error handling standards
- API route patterns

## Checklist for New Cursor Rules

**YAML Frontmatter:**
- [ ] Title field present and descriptive
- [ ] Description field present (MANDATORY - never empty or `---`)
- [ ] Tags array includes relevant categories
- [ ] Optional fields (ruleType, alwaysApply, source) added if applicable

**Project Context:**
- [ ] Tech stack clearly defined
- [ ] Version numbers specified
- [ ] Key dependencies listed

**Code Style:**
- [ ] Component style specified (functional/class)
- [ ] Export style (named/default)
- [ ] File naming convention
- [ ] Specific to project (not generic)

**Patterns:**
- [ ] At least 3 code examples
- [ ] Cover most common tasks
- [ ] Include error handling pattern
- [ ] Show project-specific conventions

**Organization:**
- [ ] Logical section headers
- [ ] Scannable (not wall of text)
- [ ] Examples are complete and runnable
- [ ] Anti-patterns included

**Testing:**
- [ ] Tested with AI assistant
- [ ] AI follows conventions correctly
- [ ] Updated after catching mistakes

---

**Remember:** Cursor rules are living documents. Update them as your project evolves and patterns emerge.
