Skillwright
 Blog

May 28, 2026 · 12 min read · by Harish Ganapathi

Best .cursorrules Examples by Stack (2026)

Every framework directory on the internet will hand you a 300-line .cursorrulesdump. That’s not the hard part. The hard part is knowing which rules actually change how Cursor codes for your stack — and which ones are filler the model already knows. This is a curated set: one tight, opinionated example per stack, plus the reasoning behind each rule so you can adapt instead of copy-paste.

Key takeaways

  • These are written as modern .cursor/rules/*.mdc files, but the content works verbatim in a legacy .cursorrules file too.
  • Good rules are specific and example-driven, scoped with globs, and kept under ~500 lines. Vague style-guide prose is wasted context.
  • Start with a base rule every project needs(types, size limits, testing, a don’t-do list), then layer stack-specific rules on top.
  • The same standards belong in CLAUDE.md, AGENTS.md, and copilot-instructions — author once and compile to every tool instead of maintaining four copies.

Quick format note before the examples: if you’re fuzzy on the difference between the legacy root file and the .mdc directory, the four rule types, or how globs and alwaysApply work, read the Cursor rules guide first. Everything below assumes you know the mechanics and just want the content.

What makes a good cursor rule

Before the per-stack examples, the rules about rules. A .cursorrules file is context the model pays for on every relevant turn, so every line has to earn its place. Four principles do most of the work:

One more meta-rule from Cursor: write rules reactively. When you catch the agent making the same mistake twice, that’s the rule worth adding — not a speculative wall of conventions you imagine you’ll need.

The rules every project needs

Regardless of stack, there’s a base set worth making alwaysApply: true. These are the standards that the model violates by default and that apply to literally every file you write: type strictness, size limits, a testing expectation, and an explicit list of things it must never do without asking.

---
description: Baseline engineering standards for this repo
alwaysApply: true
---

# Standards
- Strict types. No `any`, no unchecked `as` casts, no `@ts-ignore`
  without a one-line justification comment.
- Functions under ~50 lines; files under ~400. Extract, don't sprawl.
- Prefer pure functions and immutable data. Don't mutate arguments.
- Handle errors explicitly. Never swallow a catch with an empty block.

# Testing
- New behavior ships with a test. Co-locate as `*.test.ts`.
- Don't delete or weaken a failing test to make it pass.

# Never without asking
- Don't run `install`, `migrate`, or any destructive git command.
- Don't add a dependency when the stdlib or an existing dep covers it.
- Don't reformat files you weren't asked to touch.

That last block is the highest-leverage section in any rules file. A precise “never” list prevents the most annoying agent behaviors — silent dependency creep, unrequested reformats, surprise migrations — far more reliably than any positive instruction.

Next.js (App Router) cursor rules

The App Router is where the model’s training data fights you hardest: there’s years of Pages Router and client-component habit baked in. The rules that matter are the ones that push it toward server components, server-side data fetching, and route handlers instead of the old patterns.

---
description: Next.js App Router conventions
globs: ["app/**/*.tsx", "app/**/*.ts"]
alwaysApply: false
---

- Server Components by default. Add `"use client"` only when you need
  state, effects, or browser APIs — and push it as far down the tree
  as possible.
- Never fetch data in a `useEffect`. Fetch in an async Server Component
  or a Route Handler. Mutations go through Server Actions.
- Route Handlers live in `app/**/route.ts`, not the legacy `pages/api`.
- Use `next/image` and `next/font`; don't hand-roll image or font loading.
- Read params/searchParams from props, not `useRouter`, in Server Components.

The useEffect rule alone justifies the file. Left to its own devices the model will reach for a client-side fetch on instinct; this rule redirects it to the pattern the App Router was built for.

React cursor rules

For non-Next React, the win is steering away from legacy class-component and lifecycle thinking and toward modern hooks discipline. Keep these framework-agnostic so they compose with whatever meta-framework or bundler sits on top.

---
description: React component conventions
globs: ["src/**/*.tsx"]
alwaysApply: false
---

- Function components and hooks only. No class components.
- One component per file; co-locate its hook and types.
- Derive state during render instead of syncing it in `useEffect`.
  Most `useEffect`s for "computing a value" are a bug.
- Memoize only after a measured problem — no reflexive `useMemo`/`useCallback`.
- Lift shared state to the nearest common parent or a store; don't prop-drill
  more than two levels.
- Keys must be stable IDs, never array indices.

TypeScript cursor rules

Your base rule already bans any. A dedicated TypeScript rule goes further and encodes the type-design decisions the model won’t make on its own — the difference between code that compiles and code that’s actually type-safe.

---
description: TypeScript style and type-safety rules
globs: ["**/*.ts", "**/*.tsx"]
alwaysApply: false
---

- Prefer `type` aliases for unions/intersections; `interface` for object
  shapes that may be extended. Be consistent within a file.
- No `enum`. Use `as const` objects or string-literal unions.
- Use `unknown` over `any` at boundaries, then narrow with a guard.
- Validate external data (API, env, JSON) with a schema — never trust a cast.
- Make illegal states unrepresentable: discriminated unions over optional-soup.
- Avoid non-null assertions (`!`); handle the `null` case explicitly.

Reuse tip

The no enum and discriminated unions rules are exactly the kind of opinion that should be identical in your Claude Code and Copilot setups too. If you maintain those files separately, this is the rule that drifts first — see the portability section below.

Python / FastAPI cursor rules

FastAPI rewards a specific shape — Pydantic models everywhere, async-first handlers, and dependency injection instead of global state — and the model only follows it consistently if you say so. These are the rules that produce idiomatic FastAPI rather than Flask-flavored FastAPI.

---
description: FastAPI service conventions
globs: ["app/**/*.py", "src/**/*.py"]
alwaysApply: false
---

- Every request/response body is a Pydantic model. No raw dicts crossing
  the boundary; no manual `request.json()` parsing.
- Path operations are `async def`. Use async DB and HTTP clients
  (asyncpg/httpx), never a blocking call inside an async handler.
- Inject dependencies (DB sessions, auth, settings) via `Depends`,
  not module-level singletons.
- Type-hint everything; let FastAPI generate the OpenAPI schema.
- Raise `HTTPException` with explicit status codes; don't return bare dicts
  for errors.
- Settings come from a `pydantic-settings` `BaseSettings`, read from env.

The blocking-call rule is the subtle one. The model will happily drop a synchronous database call into an async def handler, which silently stalls the event loop. Spelling it out is the only reliable fix.

Node.js / Express cursor rules

Express gives you nothing for free, so the rules are about imposing structure the framework declines to: real async error handling, input validation at the edge, and middleware order that doesn’t leak stack traces to clients.

---
description: Express API conventions
globs: ["src/routes/**/*.ts", "src/middleware/**/*.ts"]
alwaysApply: false
---

- Use `express.Router()` per resource; keep handlers thin and push logic
  into a service layer.
- Wrap async handlers so rejected promises reach the error middleware —
  no unhandled rejections, no `try/catch` boilerplate in every route.
- Validate `req.body`/`req.query`/`req.params` with a schema before use.
  Reject with 400 on failure.
- One centralized error-handling middleware, registered last. Never send raw
  error objects or stack traces to the client.
- Read config from env via a validated config module; no `process.env`
  access scattered through handlers.

Tailwind CSS cursor rules

Tailwind rules are short but high-impact, because the failure mode is so consistent: the model invents arbitrary values and one-off colors instead of using your design tokens. These rules keep generated markup on the system.

---
description: Tailwind utility conventions
globs: ["**/*.tsx", "**/*.jsx", "**/*.html"]
alwaysApply: false
---

- Use theme tokens, not arbitrary values. Prefer `p-4` over `p-[17px]`
  and `text-accent` over `text-[#3b82f6]`.
- No inline `style` attributes for anything Tailwind can express.
- Compose repeated utility clusters into a component, not a copy-pasted
  className string.
- Order responsive/state variants consistently: base → `sm:` → `md:` →
  `hover:` → `dark:`.
- Don't fight the design system — if a token is missing, ask before
  hardcoding a value.

The arbitrary-value ban is what keeps a codebase from slowly accumulating p-[13px] one-offs that no amount of design-token discipline can claw back later.

Go cursor rules

Go has the strongest community consensus of any language here, which means the model usually knows the idioms — it just doesn’t apply them under pressure. These rules encode the conventions that separate Go that compiles from Go a reviewer would approve.

---
description: Go conventions
globs: ["**/*.go"]
alwaysApply: false
---

- Wrap errors with context: `fmt.Errorf("loading config: %w", err)`.
  Always wrap with `%w`, never `%v`, when propagating.
- Check every error. No `_ = err`. Handle or return, don't ignore.
- No naked returns in functions longer than a few lines.
- Accept interfaces, return concrete types.
- Pass `context.Context` as the first argument to anything doing I/O.
- No `panic` in library code; reserve it for truly unrecoverable startup.
- Keep zero values useful; avoid constructors that just set defaults.

The %w rule is the one that pays off in production. Wrapped errors let callers use errors.Is and errors.As; an error formatted with %v is a dead string that breaks the chain.

Rust cursor rules

Rust’s borrow checker keeps the model honest about a lot, but it says nothing about error handling or panic discipline — which is exactly where AI-generated Rust goes wrong. These rules target the gap between “the compiler is happy” and “this is production Rust.”

---
description: Rust conventions
globs: ["**/*.rs"]
alwaysApply: false
---

- No `.unwrap()` or `.expect()` in library code. Propagate with `?`
  and return `Result`. Reserve unwrap for tests and prototypes.
- Define error types with `thiserror`; use `anyhow` only at the
  application boundary.
- Prefer iterators and `?` over manual loops and early-return ladders.
- Borrow (`&T`) instead of cloning; reach for `.clone()` only when
  ownership genuinely needs to move.
- Derive `Debug` on public types; keep `unsafe` out unless justified
  with a `// SAFETY:` comment.
- Run `clippy` mentally — flag obvious lints before I do.

The unwrap rule is non-negotiable in shared code. A single .unwrap() in a library turns a recoverable error into a panic that takes down the caller — the model reaches for it constantly unless told not to.

Reuse these rules in Claude Code & Copilot

Here’s the part the framework directories never mention. None of these rules are Cursor rules in any real sense — they’re your engineering standards, which happen to be written in Cursor’s format today. The moment you also run Claude Code or Copilot, the exact same “async handlers only,” “no unwrapin libs,” “Pydantic at the boundary” rules have to exist in CLAUDE.md, AGENTS.md, and .github/copilot-instructions.md too — and then stay in sync forever.

That sync problem is where the time goes. You fix the FastAPI rule in Cursor, forget to mirror it into CLAUDE.md, and now two of your tools disagree about how to write a handler. Multiply by every stack and every repo and you have a maintenance tax that grows with your team.

Two ways out. The one-time fix: convert your .cursorrules into CLAUDE.md and the other formats. The durable fix: keep a single canonical library and compile it to every tool. Skillwright does the latter — author a rule once, deploy it to Cursor, Claude Code, Windsurf, and Copilot, and update everywhere from one place. For the full strategy, read how to manage AI coding rules across tools, or grab ready-made rule templates and start from a known-good base.

Frequently asked questions

Where do I put my .cursorrules file?

The legacy .cursorrules file goes at your repo root — Cursor always loads it. The modern equivalent is a directory of .mdc files at .cursor/rules/, each scoped with frontmatter. Both live in version control so your whole team gets them. For anything beyond a trivial project, prefer .cursor/rules.

Should I have one big .cursorrules or multiple .mdc rules?

Multiple. A single always-on file eventually contradicts itself and burns context on rules that don't apply to the current task. Split by concern — a base rule that's always on, plus stack- or path-scoped .mdc files with globs so they only load when you touch matching files. Keep each rule under ~500 lines.

Do cursorrules examples work in .cursor/rules?

Yes — the content is the same; only the packaging changes. Take the body of any .cursorrules example, drop it into a .cursor/rules/<name>.mdc file, and add frontmatter (description, globs, alwaysApply) so Cursor knows when to apply it. A plain .md file in that directory is ignored because it has no frontmatter.

How do I use cursor rules in a monorepo?

Put a base rule at the root .cursor/rules/ and add package-scoped rules with globs like apps/web/** or services/api/**. Cursor auto-attaches a rule when you touch a file matching its globs, so your Next.js rules fire in the web app and your FastAPI rules fire in the Python service — without you switching anything.

Can I reuse my cursor rules in Claude Code or Copilot?

The standards are identical, but the files aren't. Cursor reads .cursor/rules/*.mdc, Claude Code reads CLAUDE.md, Copilot reads .github/copilot-instructions.md, and most tools also read AGENTS.md. You can convert your .cursorrules into each format by hand, or keep one canonical library and compile to all of them — which is what Skillwright does.