Table of Contents
The Problem: Why Your Copy-Pasted Rules Don't Work
You found a "Best Cursor Rules for React" template on GitHub. You copied it into .cursorrules. Then you asked Cursor to create a Next.js Server Component.
It generated a client component with useState and useEffect.
You asked for a form with Server Actions. It created an API route and client-side fetch calls instead.
You requested an App Router page. It used Pages Router structure.
Here's why: Generic React rules don't understand Next.js architecture. They assume everything runs client-side because that's "normal React" in AI training data.
Next.js 13+ with App Router is fundamentally different:
Server Components run on the server by default
Client Components need explicit
'use client'Server Actions handle mutations without API routes
File conventions determine routing (
page.tsx,layout.tsx,loading.tsx)
This article gives you framework-specific Cursor Rules that actually work for Next.js App Router projects.
The 5 Patterns Cursor Gets Wrong (Without Proper Rules)
Pattern #1: Using Client State for Server Data
What Cursor generates (WRONG):
tsx
'use client';
export default function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`).then(res => res.json()).then(setUser);
}, [userId]);
return <div>{user?.name}</div>;
}What it should be (CORRECT):
tsx
// Server Component - fetches on server, no loading state needed
async function UserProfile({ userId }: { userId: string }) {
const user = await db.users.findUnique({ where: { id: userId } });
return <div>{user.name}</div>;
}Why this matters: Server Components eliminate waterfalls, improve performance, and reduce JavaScript bundle size.
Pattern #2: Creating API Routes Instead of Server Actions
What Cursor generates (WRONG):
tsx
// Unnecessary API route + client-side form
export async function POST(request: Request) {
const body = await request.json();
await db.users.update({ where: { id: body.id }, data: body });
return Response.json({ success: true });
}What it should be (CORRECT):
tsx
// Server Action - simpler, progressively enhanced
'use server';
export async function updateUser(formData: FormData) {
const name = formData.get('name') as string;
await db.users.update({ where: { id: '1' }, data: { name } });
revalidatePath('/users');
}Why this matters: Server Actions work without JavaScript, reduce code, and simplify architecture.
Pattern #3: Wrong File Structure
What Cursor generates (WRONG):
app/users/UserList.tsx ❌ Won't be routable
app/users/UserDetail.tsx ❌ Wrong locationWhat it should be (CORRECT):
app/users/page.tsx ✅ Route component
app/users/loading.tsx ✅ Loading UI
app/users/_components/ ✅ Private folder
UserList.tsx ✅ Reusable componentPattern #4: Missing or Unnecessary 'use client'
WRONG: Adding 'use client' everywhere (makes everything client-side) WRONG: Forgetting 'use client' when using hooks (causes errors)
CORRECT: Only use 'use client' when you need:
React hooks (
useState,useEffect, etc.)Event handlers (
onClick,onChange)Browser APIs (
window,localStorage)
Get Production-Ready Next.js + Cursor Resources
You now understand why generic rules fail and what framework-specific rules look like. But implementing this across your team takes time:
Setting up rule files correctly
Testing across different scenarios
Training developers on Next.js patterns
Keeping rules updated with Next.js changes
What if you had access to battle-tested setups that already work?
✅ Complete Cursor Rules templates for Next.js 14+ App Router
✅ Security rules preventing common vulnerabilities
✅ MCPs that understand Next.js architecture
✅ Expert consultants specializing in Next.js + AI workflows
✅ Community-tested prompts for Server Components and Actions
Stop copying broken templates. Get rules tested in production Next.js apps.
Pattern #5: Not Using Next.js Optimizations
WRONG:
tsx
<img src="/hero.jpg" alt="Hero" />
<a href="/about">About</a>CORRECT:
tsx
import Image from 'next/image';
import Link from 'next/link';
<Image src="/hero.jpg" alt="Hero" width={800} height={600} />
<Link href="/about">About</Link>The Production-Ready Cursor Rules Setup
Create this file structure:
your-project/
.cursor/
rules/
nextjs-core.mdc
server-components.mdc
client-components.mdcRule 1: Core Next.js Principles
Create .cursor/rules/nextjs-core.mdc:
markdown
---
description: Core Next.js App Router principles
globs: ["app/**/*.tsx", "app/**/*.ts"]
alwaysApply: true
---
# Next.js App Router Core Rules
## Mental Model
- Server Components by DEFAULT (no 'use client' needed)
- Add 'use client' ONLY for interactivity/hooks
- Use Server Actions for mutations (not API routes)
- Fetch data directly in Server Components
## File Conventions
- page.tsx = route component
- layout.tsx = shared layout
- loading.tsx = loading UI with Suspense
- error.tsx = error boundary
- _folder/ = private components (not routable)
- actions.ts = Server Actions
## Before Writing Code
1. Can this be a Server Component? (default: YES)
2. Do I need hooks/events? (if NO → Server Component)
3. Should this be a Server Action vs API route?
4. Am I following Next.js file structure?
## Critical Rules
- Check existing code with codebase_search first
- NEVER add 'use client' unless absolutely necessary
- Use async Server Components for data fetching
- Forms should work without JavaScript when possibleRule 2: Server Components
Create .cursor/rules/server-components.mdc:
markdown
---
description: Server Component patterns
globs: ["app/**/page.tsx", "app/**/layout.tsx"]
alwaysApply: false
---
# Server Components
## Pattern: Async Data Fetching
```typescript
async function ProductPage({ params }: { params: { id: string } }) {
const product = await db.products.findUnique({
where: { id: params.id }
});
return {product.name};
}
```
## NEVER in Server Components
❌ NO useState, useEffect, or any hooks
❌ NO event handlers (onClick, onChange)
❌ NO browser APIs (window, document)
❌ NO 'use client' directive
## Composition
Server Components can render Client Components:
```typescript
import { InteractiveButton } from './_components/InteractiveButton';
async function Page() {
const data = await fetchData();
return (
{data.title}
{/* Client Component */}
);
}
```
## Streaming with Suspense
```typescript
import { Suspense } from 'react';
export default function Page() {
return (
}>
);
}
```Rule 3: Client Components
Create .cursor/rules/client-components.mdc:
markdown
---
description: Client Component patterns for interactivity
globs: ["app/**/_components/*.tsx"]
alwaysApply: false
---
# Client Components
## When to Add 'use client'
ONLY when you need:
- Hooks (useState, useEffect, useContext)
- Event handlers (onClick, onChange, onSubmit)
- Browser APIs (window, localStorage)
- Third-party libraries using hooks
## Pattern: Minimal Client Component
```typescript
'use client'; // Required at TOP of file
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
);
}
```
## Push 'use client' Down the Tree
Keep it at the leaf level:
```typescript
// ✅ GOOD: Only Counter is client-side
export default function Page() { // Server Component
return (
{/* Server */}
{/* Client */}
{/* Server */}
);
}
```
## Client Data Fetching (when necessary)
```typescript
'use client';
import { useState, useEffect } from 'react';
export function ClientData() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData);
}, []);
return {data?.content};
}
```
But prefer Server Components with Suspense instead.Additional Essential Rules
Server Actions Template
Create .cursor/rules/server-actions.mdc:
markdown
---
description: Server Actions for mutations
globs: ["app/**/actions.ts"]
alwaysApply: false
---
# Server Actions
## Pattern: Form with Server Action
```typescript
// actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createProduct(formData: FormData) {
const name = formData.get('name') as string;
// Validation
if (!name) return { error: 'Name required' };
// Mutation
await db.products.create({ data: { name } });
// Revalidate cache
revalidatePath('/products');
return { success: true };
}
// page.tsx
import { createProduct } from './actions';
export default function NewProduct() {
return (
Create
);
}
```
## When to Use
✅ Form submissions
✅ Data mutations (create/update/delete)
✅ Progressive enhancement needed
## When NOT to Use
❌ Data fetching (use Server Components)
❌ GET requests (use Route Handlers)
❌ Webhooks (use Route Handlers)Route Handlers Template
markdown
---
description: API Route Handlers
globs: ["app/api/**/route.ts"]
alwaysApply: false
---
# Route Handlers
## Pattern: RESTful API
```typescript
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await db.users.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await db.users.findUnique({
where: { id: params.id }
});
return NextResponse.json(user);
}
```
## When to Use
✅ External API integrations
✅ Webhooks
✅ OAuth callbacks
✅ Non-HTML responses
## When NOT to Use
❌ Simple form submissions (use Server Actions)Quick Verification Checklist
After Cursor generates Next.js code, verify:
Server Components:
No
'use client'(unless needed for interactivity)Uses
asyncif fetching dataNo hooks or event handlers
Data fetched directly, not with useEffect
Client Components:
Has
'use client'at top of fileOnly used when hooks/events needed
Pushed to leaf level (smallest possible)
File Structure:
page.tsxfor routeslayout.tsxfor layouts_components/for reusable componentsactions.tsfor Server Actions
Optimizations:
next/imageinstead of<img>next/linkinstead of<a>(internal links)Type safety with TypeScript interfaces
Error handling in Server Actions
Common Mistakes to Avoid
Mistake #1: Making Everything Client-Side
Don't add 'use client' to every file. Server Components are the default and should be your default choice.
Mistake #2: Using API Routes for Simple Forms
Server Actions are simpler and more performant for mutations. Save API routes for external integrations.
Mistake #3: Ignoring File Conventions
Next.js App Router uses specific file names. page.tsx is not optional—it's the convention.
Mistake #4: Not Revalidating After Mutations
Always call revalidatePath() or revalidateTag() after data changes in Server Actions.
Mistake #5: Fetching Data Client-Side by Default
Server Components can fetch data directly. Client-side fetching should be the exception, not the rule.
Build Next.js Apps the Right Way with AI
You've learned the framework-specific rules that make Cursor actually work for Next.js. You understand Server Components, Server Actions, and the architectural patterns that matter.
Now make it effortless.
The Lovable Directory is your resource hub for Next.js + AI development:
📋 Complete rule templates for Next.js 14+ (ready to copy)
🔒 Security frameworks preventing Next.js vulnerabilities
🤖 MCPs and tools optimized for App Router
👥 Expert consultants who specialize in Next.js workflows
📚 Tested prompts for Server Components, Actions, and Route Handlers
🚀 Tool comparisons for Next.js-optimized AI assistants
Stop wasting time with generic templates that don't understand Next.js.
Join Lovable Directory and access the framework-specific resources that separate fast-shipping teams from debugging teams.
Build Next.js apps the way they're meant to be built—with AI that actually understands the framework.
Key Takeaways
Generic React rules fail for Next.js because they don't understand Server Components, App Router conventions, or the server-client boundary.
The 5 common mistakes Cursor makes: using client state for server data, creating API routes instead of Server Actions, wrong file structure, missing/unnecessary 'use client', and not using Next.js optimizations.
Server Components are the default in Next.js App Router—only add 'use client' when you need hooks, events, or browser APIs.
Server Actions replace API routes for most mutations, providing simpler code and progressive enhancement.
File conventions matter:
page.tsxfor routes,layout.tsxfor layouts,_components/for private components,actions.tsfor Server Actions.Always optimize: Use
next/image,next/link, and other Next.js-specific components for best performance.Verification is critical: Check every AI-generated file against the framework-specific patterns before shipping.
Final Thought
Next.js App Router represents a paradigm shift in how we build React applications. AI coding assistants like Cursor are incredibly powerful—but only when they understand the framework you're actually using.
Generic rules make Cursor generate code that looks like React but doesn't work like Next.js. Framework-specific rules make it generate code that leverages Server Components, Server Actions, and App Router conventions properly.
The difference between developers shipping Next.js apps fast and those constantly debugging isn't skill—it's setup.
Get your Cursor Rules right, and Next.js + AI becomes your competitive advantage.
The templates are ready. The patterns work. The framework is clear.
All that's left is implementation.