5 min read

Cursor Rules

Cursor Rules
Photo by Aerps.com / Unsplash

What are Cursor rules?

Cursor rules are special instructions you provide to the editor's AI to guide its behavior, such as code generation, editing, and responses. They act as a persistent context or a set of guidelines, ensuring the AI understands the specific conventions, libraries, and architectural patterns of your project or your personal preferences.

Why do we need them? 🤔

We need Cursor rules to make the AI more context-aware and efficient. Without them, you'd have to repeat instructions in every prompt. Rules allow you to "teach" the AI about your specific needs once, leading to more consistent, accurate, and relevant assistance.

For example, you can use rules to:

  • Tell the AI to always use a specific state management library (like Zustand or Redux) in a React project.
  • Enforce a coding style, like using arrow functions instead of function declarations.
  • Provide context about your project's file structure or API design.
  • Define custom commands or shortcuts for repetitive tasks.

Types of rules in Cursor

There are two main types of rules in Cursor, each serving a different purpose: project rules and user rules.

Project rules:

Project rules are defined for a specific workspace or repository. They are stored within the project's directory in a file named rule.mdc located inside a .cursor folder (.cursor/rules/rule.mdc).

The key feature of project rules is that they are shared with the codebase. When you commit the .cursor folder to your version control system (like Git), everyone who clones the repository and uses Cursor will automatically have the same AI guidelines. This is perfect for teams, as it ensures all members get consistent AI behavior that aligns with the project's standards. 🤝

The rule.mdc file uses a specific markdown format where you define a frontmatter configuration block followed by the prompt instructions for the AI.

Showcase: Applying a project rule

Let's imagine you're starting a new component for a Next.js project. The prompt you provide the AI is simple:

Create a simple counter component.

Without any specific project rules, the AI might generate something generic like this:

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
};

export default Counter;

Now, let's apply a project rule to enforce a specific architectural pattern. We will create a rule.mdc file to guide the AI to use Zustand for state management and TailwindCSS for styling.

File: .cursor/rules/rule.mdc

---
description: Cursor rules for a Next.js 15+ project with Supabase Auth and modern React/Tailwind best practices
globs: '***/*.ts, */.tsx, */.js, */.jsx'
alwaysApply: true
---
You are a Senior Front-End Developer and an Expert in React, Next.js, JavaScript, TypeScript, HTML, CSS and modern UI/UX frameworks (e.g., TailwindCSS, shadcn, Radix). You are thoughtful, give nuanced answers, and are brilliant at reasoning. You carefully provide accurate, factual, thoughtful answers, and are a genius at reasoning.
When creating components, you must:
1. *Always use a state management library:** Prefer Zustand for all component state unless otherwise specified.
2. *Apply modern styling:** Use TailwindCSS classes for all styling. Do not use inline styles or CSS modules.
3. *Adhere to best practices:** Use arrow functions for components and destructure props at the component signature.***

With this rule applied, when you ask the AI to "create a simple counter component", it now has the project context and will generate the code that aligns with your team's standards:

import React from 'react';
import { create } from 'zustand';

// Define the state store using Zustand
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

const Counter = () => {
  // Use the store to access state and actions
  const { count, increment, decrement } = useCounterStore();

  return (
    <div className="flex flex-col items-center justify-center p-4 space-y-4 bg-gray-100 rounded-lg shadow-md">
      <h2 className="text-xl font-semibold text-gray-800">Zustand Counter</h2>
      <p className="text-3xl font-bold text-blue-600">Count: {count}</p>
      <div className="flex space-x-2">
        <button
          onClick={increment}
          className="px-4 py-2 font-bold text-white transition-colors bg-blue-500 rounded-md hover:bg-blue-600"
        >
          Increment
        </button>
        <button
          onClick={decrement}
          className="px-4 py-2 font-bold text-white transition-colors bg-red-500 rounded-md hover:bg-red-600"
        >
          Decrement
        </button>
      </div>
    </div>
  );
};

export default Counter;

User rules

User rules, on the other hand, are global and personal to you. They apply to all projects you open in Cursor on your machine. These rules are not stored within a project folder and are not shared with other collaborators.

You would use user rules to define your personal coding preferences, custom commands, or general instructions that you want the AI to follow regardless of which project you're working on. For instance, you could set a user rule to always have the AI explain its code changes in a certain format or to prefer a specific commenting style. These rules are about tailoring the AI to your individual workflow. 🧑‍💻

Showcase: Adding a user rule

Let's continue with our counter component. The project rule has been applied, but you have a personal preference as a developer to always include JSDoc comments for documentation. This is a preference that should apply to all your projects, not just this specific one.

You would create a new user rule (via the Cursor interface) with instructions to add documentation.

New User Rule:

You are an expert at writing concise and accurate JSDoc comments for all
functions and components.
Every function or component you write must include a JSDoc block detailing
its purpose, parameters, and return value.

Now, with both the project rule (use Zustand and Tailwind) and your new user rule (add JSDoc comments) active, when you ask the AI to generate the counter component, you get a result that combines both sets of instructions:

import React from 'react';
import { create } from 'zustand';

/**
 * @typedef {object} CounterStoreState
 * @property {number} count - The current count value.
 * @property {() => void} increment - A function to increment the count.
 * @property {() => void} decrement - A function to decrement the count.
 */

// Define the state store using Zustand
/**
 * A Zustand store for managing the counter's state.
 * @type {import('zustand').UseBoundStore<import('zustand').StoreApi<CounterStoreState>>}
 */
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

/**
 * A simple counter component that uses Zustand for state management and TailwindCSS for styling.
 * It demonstrates how to interact with the global counter store.
 * @returns {JSX.Element} The Counter component.
 */
const Counter = () => {
  // Use the store to access state and actions
  const { count, increment, decrement } = useCounterStore();

  return (
    <div className="flex flex-col items-center justify-center p-4 space-y-4 bg-gray-100 rounded-lg shadow-md">
      <h2 className="text-xl font-semibold text-gray-800">Zustand Counter</h2>
      <p className="text-3xl font-bold text-blue-600">Count: {count}</p>
      <div className="flex space-x-2">
        <button
          onClick={increment}
          className="px-4 py-2 font-bold text-white transition-colors bg-blue-500 rounded-md hover:bg-blue-600"
        >
          Increment
        </button>
        <button
          onClick={decrement}
          className="px-4 py-2 font-bold text-white transition-colors bg-red-500 rounded-md hover:bg-red-600"
        >
          Decrement
        </button>
      </div>
    </div>
  );
};

export default Counter;

As you can see, with the application of both the project rule and your personal user rule, we've achieved a result that perfectly aligns with both team conventions and your individual preferences, without specifying either in the prompt.