Problem: Cursor Suggests the Wrong Patterns for Your Next.js Project
You ask Cursor to generate a server component and it scaffolds a client component with useEffect. You request a route handler and it writes Pages Router syntax. You're on Next.js 15 with the App Router, TypeScript strict mode, and Tailwind — but Cursor acts like it's 2022.
The fix is a .cursorrules file. It gives Cursor project-specific context so every suggestion fits your actual stack.
You'll learn:
- How
.cursorrulesworks and where it lives - A complete, production-ready ruleset for Next.js 15 App Router
- How to extend rules for your specific conventions (auth, data fetching, component library)
Time: 20 min | Difficulty: Intermediate
Why Cursor Gets Next.js Wrong Without Rules
Cursor's base model has broad training data covering every version of Next.js, React, and every routing convention. Without project context it hedges — defaulting to the most common patterns it's seen, which skews toward older Pages Router code.
Symptoms:
"use client"added to components that don't need itgetServerSidePropssuggested instead ofasyncServer Componentsimport { useRouter } from 'next/router'instead ofnext/navigation- Missing
'use server'on Server Actions - Generic file names that ignore your naming conventions
A .cursorrules file is read at the start of every Cursor session. It acts as a persistent system prompt scoped to your project.
Solution
Step 1: Create the .cursorrules File
Place it at your project root — same level as package.json.
touch .cursorrules
Cursor reads this file automatically. No config, no plugin, no restart required.
Step 2: Add the Next.js 15 Base Ruleset
Copy this into .cursorrules. Each section is commented so you can trim what doesn't apply.
# Next.js 15 — App Router — Cursor Rules
# Stack: Next.js 15, React 19, TypeScript 5 strict, Tailwind CSS 4, Drizzle ORM
## Core Framework Rules
- Use the App Router exclusively. Never suggest Pages Router (`pages/` directory, `getServerSideProps`, `getStaticProps`, `_app.tsx`).
- Default to React Server Components (RSC). Only add `"use client"` when the component uses browser APIs, event handlers, or React hooks.
- For data fetching: fetch directly in async Server Components. Do not use `useEffect` + `useState` for data that can be fetched server-side.
- Use `next/navigation` for routing hooks: `useRouter`, `usePathname`, `useSearchParams`. Never import from `next/router`.
- Server Actions require `"use server"` at the top of the file or function. Use them for form submissions and data mutations.
- Use `<Link href="">` from `next/link` for internal navigation. Never use `<a>` tags for internal routes.
## TypeScript Rules
- Strict mode is enabled. Never use `any`. Use `unknown` and narrow with guards.
- Use `type` for object shapes and unions. Use `interface` only when extending.
- All component props must be explicitly typed. No implicit prop types.
- Use `satisfies` operator to validate object literals against types without widening.
- React component return type: let TypeScript infer it. Do not manually annotate `JSX.Element` or `React.FC`.
## File and Folder Conventions
- Components: `PascalCase.tsx` — e.g., `UserCard.tsx`
- Pages and layouts: `lowercase` — `page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`
- Utilities and hooks: `camelCase.ts` — e.g., `formatDate.ts`, `useTheme.ts`
- Server Actions: `actions.ts` inside the relevant feature folder
- Co-locate components with their route: `/app/dashboard/UserCard.tsx`
- Shared UI: `/components/ui/` — atomic, no business logic
- Feature components: `/components/features/` — business logic allowed
## Styling Rules
- Use Tailwind CSS utility classes. No inline styles, no CSS modules unless specifically asked.
- Responsive design: mobile-first. Start with base styles, add `md:` and `lg:` breakpoints.
- Dynamic classes: use `clsx` or `cn` (shadcn helper). Never string-concatenate class names.
- Do not use arbitrary values (e.g., `w-[347px]`) unless no standard value fits. Prefer the design scale.
## Data Fetching Patterns
- Use `fetch()` with Next.js cache options in Server Components: `{ cache: 'force-cache' }`, `{ next: { revalidate: 60 } }`, or `{ cache: 'no-store' }`.
- Parallel data fetching: use `Promise.all()` for independent fetches — never await sequentially.
- Database queries: use Drizzle ORM. Raw SQL only for complex queries Drizzle can't express.
- Wrap database calls in try/catch. Return typed result objects, not thrown errors, to the UI layer.
## Error Handling
- Use `error.tsx` files for route-level error boundaries.
- Use `notFound()` from `next/navigation` for 404 cases — do not throw manually.
- Server Actions return `{ success: boolean; error?: string }`. Never throw from a Server Action called by a form.
## Performance
- Images: always use `<Image>` from `next/image`. Set explicit `width` and `height` or use `fill` with a sized container.
- Fonts: load via `next/font`. Never import fonts from Google Fonts CDN directly.
- Heavy client components: wrap in `dynamic(() => import(...), { ssr: false })` when they are not needed on initial render.
- Keep Client Component boundaries as deep as possible in the tree.
## Code Style
- No default exports except for Next.js special files (`page.tsx`, `layout.tsx`, `error.tsx`, `loading.tsx`). Use named exports for everything else.
- Async Server Components: declare as `async function ComponentName()`.
- Short functions over long ones. Extract logic into clearly named utilities.
- Comments explain WHY, not WHAT. Skip obvious comments.
Step 3: Add Your Project-Specific Extensions
The base ruleset covers the framework. Add a second section for your project's decisions. Here are common extensions:
Auth (Clerk or NextAuth)
## Auth
- Auth provider: Clerk. Import from `@clerk/nextjs`.
- Protect server-side routes with `auth()` from `@clerk/nextjs/server`.
- For client-side user data: use `useUser()` hook. Never pass the full user object as a prop through multiple layers.
- Auth middleware is in `middleware.ts` at project root. Do not duplicate auth checks in layouts.
Component Library (shadcn/ui)
## UI Components
- Use shadcn/ui components from `/components/ui/`. Do not rebuild primitives like Button, Dialog, or Input from scratch.
- Extend shadcn components via the `className` prop and `cn()`. Do not modify the source files in `/components/ui/` directly.
- Form validation: use `react-hook-form` with `zod` resolver. The schema lives next to the form file.
API Conventions
## API Route Handlers
- Route handlers live in `/app/api/[resource]/route.ts`.
- Always type the return value: `return NextResponse.json<ResponseType>(data)`.
- Validate request bodies with Zod before processing. Return 400 on validation failure.
- Use HTTP status codes correctly: 200 success, 201 created, 400 bad request, 401 unauthenticated, 403 forbidden, 404 not found, 500 server error.
Step 4: Commit the File
git add .cursorrules
git commit -m "chore: add Cursor rules for Next.js 15 App Router"
Commit .cursorrules to the repo. Every team member and every Cursor session on this project will use the same rules. This is the point — AI consistency across your whole team.
Step 5: Validate the Rules Are Working
Open Cursor, start a new Composer session, and run this test prompt:
Create a server component that fetches a list of users from /api/users and renders them in a table. Use TypeScript.
You should see:
async function— no"use client"fetch('/api/users', { cache: 'no-store' })or{ next: { revalidate: 60 } }- Named export, not default export
- Typed response with an explicit interface
- Tailwind classes for the table
If Cursor still suggests client-side fetching: Open the Cursor chat panel, type @.cursorrules to reference the file directly, and ask it to regenerate. This forces an explicit read of the rules in that session.
Advanced: Rule Scoping with Multiple Files
For monorepos or projects with distinct sub-apps, you can scope rules per directory using the new cursorules format (Cursor 0.45+):
# Root-level rules (applies everywhere)
.cursorrules
# App-specific rules (applies only inside /apps/web)
apps/web/.cursorrules
# Package-specific rules
packages/ui/.cursorrules
Rules are merged, with deeper files taking precedence on conflicts. Use this to set different conventions for your Next.js app vs your shared component library.
What You Learned
.cursorruleslives at project root and is read automatically — no config needed- Split rules into framework rules (shared) and project rules (your decisions)
- Named exports, RSC-first, and
next/navigationare the three rules that fix 80% of wrong suggestions - Commit the file so AI consistency is a team property, not a personal setting
Limitation: .cursorrules is a hint, not a hard constraint. For complex generation tasks, reference it explicitly with @.cursorrules in Composer. On very long sessions, paste the relevant section directly into your prompt.
Tested on Cursor 0.47, Next.js 15.2, TypeScript 5.8, macOS and Ubuntu 24.04