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 location

What it should be (CORRECT):

app/users/page.tsx            ✅ Route component
app/users/loading.tsx         ✅ Loading UI
app/users/_components/        ✅ Private folder
  UserList.tsx                ✅ Reusable component

Pattern #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.mdc

Rule 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 possible

Rule 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 async if fetching data

  • No hooks or event handlers

  • Data fetched directly, not with useEffect

Client Components:

  • Has 'use client' at top of file

  • Only used when hooks/events needed

  • Pushed to leaf level (smallest possible)

File Structure:

  • page.tsx for routes

  • layout.tsx for layouts

  • _components/ for reusable components

  • actions.ts for Server Actions

Optimizations:

  • next/image instead of <img>

  • next/link instead 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

  1. Generic React rules fail for Next.js because they don't understand Server Components, App Router conventions, or the server-client boundary.

  2. 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.

  3. Server Components are the default in Next.js App Router—only add 'use client' when you need hooks, events, or browser APIs.

  4. Server Actions replace API routes for most mutations, providing simpler code and progressive enhancement.

  5. File conventions matter: page.tsx for routes, layout.tsx for layouts, _components/ for private components, actions.ts for Server Actions.

  6. Always optimize: Use next/image, next/link, and other Next.js-specific components for best performance.

  7. 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.

Resources:


Keep Reading