Cursor rules for Next.js and TypeScript
How Cursor loads .mdc rules and AGENTS.md, a Next.js + TypeScript rule set for the App Router and a modular backend, and how to back it with lint.
The Cursor rules for Next.js and TypeScript that matter most are the ones about
where code is allowed to live, and what it is allowed to import. Most "Cursor
rules for Next.js" lists are style preferences — prefer server components, no
any, named exports — which are useful, but mostly things a linter or the type
checker already enforces. The rules that decide whether a codebase is still
maintainable after six months of AI-written changes are the architectural ones,
and this guide is about those.
Code for this guide: https://github.com/KonixDev/claude-code-guardrails — the open-source (MIT) claude-code-guardrails kit.
How Cursor loads rules (2026)
From Cursor's documentation:
- Project rules live in
.cursor/rules/and must use the.mdcextension. A plain.mdfile in that folder is ignored by the rules system. Subfolders are allowed. - Each
.mdcstarts with frontmatter. Three fields decide when the rule is included:
alwaysApply | description | globs | When the rule is used |
|---|---|---|---|
true | — | — | Always |
false | — | set | When a matching file is in context |
false | set | — | When the agent judges it relevant from the description |
false | — | — | Only when you @-mention it |
- Cursor also reads
AGENTS.mdat the project root and in subdirectories, as plain markdown. Nested files combine with their parents, with the more specific one taking precedence. - Rules apply in the order Team → Project → User, and the earlier source wins on a conflict. Cursor's own advice: keep each rule under 500 lines.
That last point changes how to organize rules: put the architecture in
AGENTS.md, which Claude Code and Codex read too, and use .mdc files for
Cursor-specific, file-scoped reminders.
A rule set for a modular Next.js + TypeScript SaaS
Assume the backend logic lives in src/modules/<context>/, each context split
into domain/, application/, infrastructure/ and interfaces/, and the
Next.js app consumes it.
AGENTS.md (excerpt — the rules every agent reads):
## Rules
1. Dependencies point inward. domain/ imports nothing outside itself;
application/ imports only its own domain/.
2. No frameworks, ORMs or SDKs in domain/ or application/. Need I/O?
Declare a port in application/, implement it in infrastructure/.
3. Across modules, only `import type`. Never call another module's
internals at runtime.
4. Done means `npm run guardrails` passes. Never disable a rule to get green..cursor/rules/architecture.mdc (auto-attached when module code is in play):
---
description: Layered architecture rules for src/modules (domain, application, infrastructure, interfaces) that ESLint enforces. Apply when creating or moving code between modules or layers.
globs: src/modules/**
alwaysApply: false
---
The full rules live in AGENTS.md. In short:
- domain/ imports nothing outside itself; application/ only its own domain/.
- No frameworks, ORMs or SDKs in domain/ or application/.
- Across modules, only `import type`.
- These are enforced by eslint.boundaries.mjs. Never disable the rule;
move the code or add a port..cursor/rules/nextjs.mdc (your app conventions, scoped to the App Router
folder — use src/app/** or apps/web/app/** if that is where yours lives):
---
description: Next.js App Router and TypeScript conventions for this repo.
globs: app/**
alwaysApply: false
---
Components
- Server components by default. Add "use client" only for state, effects,
event handlers or browser APIs, and push it to the smallest leaf component.
- Never import server-only code (database, secrets, SDKs with keys) into a
client component. Mark such modules with `import 'server-only'`.
Data and mutations
- Route handlers and server actions are thin: validate input, call an
application/ use case, map the result. No database calls in them.
- Validate every request body and action payload with the shared schemas from
packages/contracts. Never re-declare a field on the client.
- After a mutation, revalidate what changed (`revalidatePath` /
`revalidateTag`) instead of disabling caching globally.
TypeScript
- No `any`. Use `unknown` plus a type guard or a Zod parse at the boundary.
- Explicit return types on exported functions and server actions.
- Prefer named exports; default exports only where Next.js requires them
(page, layout, route segments).
Before finishing
- Run `npm run guardrails` (lint + typecheck) and make it pass.The "thin route handlers" block is what keeps database calls from spreading
across app/: the agent has one obvious place for business logic, and lint
rejects the shortcut.
Back the rules that must never break with lint
A rule file tells the agent why and where; it does not stop it (the longer argument). So every rule above that must never break also exists as a check:
// eslint.config.mjs
import guardrails from './eslint.boundaries.mjs';
export default [
// ...your existing config
...guardrails(), // layers + type-only cross-module imports
];Run it in the editor and in CI: the same config that explains the rule to a human reviewer fails the build when the agent breaks it. We walk through the exact rules in compiler-enforced DDD boundaries.
Checklist
- Architecture rules in
AGENTS.md, not duplicated per tool. -
.mdcfiles scoped withglobsinstead ofalwaysApply: true, so they don't crowd every request. - Each rule that must never break also exists as a lint rule or a CI check.
- One command (
npm run guardrails) the agent is told to run before it is done.
Get the files
The AGENTS.md, the architecture .mdc, the ESLint boundary config and a CI
workflow are in claude-code-guardrails
(MIT, copy-in). Cursor also has its own hooks (.cursor/hooks.json, with events
such as afterFileEdit and stop); the kit's hook scripts are written for
Claude Code — see Claude Code hooks to enforce your
architecture.
FAQ
Is .cursorrules still a thing?
Cursor's current rules documentation only describes .mdc files in
.cursor/rules/ and AGENTS.md. New projects should use those.
Should rules be alwaysApply: true?
Only short, truly global ones. Everything else is better scoped with globs,
so it shows up when the matching files are in play and stays out of the way
otherwise.
How long can a rule be?
Cursor recommends keeping each rule under 500 lines. Shorter, globs-scoped
rules plus a linter for the hard constraints beat one exhaustive document.
useDeploy is a Next.js + TypeScript SaaS base that ships AGENTS.md, this
Cursor rule and the ESLint boundaries on top of auth, organizations and billing. See what's included.