React Next.js CLAUDE.md cursor rules AI coding rules TypeScript App Router Claude Code

AI Coding Rules for React and Next.js Projects 2026: CLAUDE.md and .cursorrules Templates

The Prompt Shelf ·

AI coding rules for React and Next.js projects are more nuanced than most frameworks because the correct pattern depends heavily on context: whether a component should be a server or client component, whether a data fetch belongs in a Server Action or an API route, whether state belongs in React state or a URL search param. An AI assistant without clear rules will make different choices session to session, and some of those choices have significant performance implications.

This guide provides practical CLAUDE.md templates and .cursorrules examples for React and Next.js 14+ (App Router). Browse React and Next.js rules from real projects in our gallery.

The Most Important Rule: Server vs. Client Components

This is the question that trips up AI tools most in modern Next.js projects. The default answer should be “server component” with very specific exceptions:

## React Component Model (Next.js App Router)

### Default: Server Components
All components are server components by default. Do NOT add "use client" unless
one of these conditions is true:
- The component uses React hooks (useState, useEffect, useReducer, etc.)
- The component uses browser APIs (window, document, localStorage, etc.)
- The component handles user events interactively (onChange, onClick with state)
- The component requires real-time updates (useWebSocket, etc.)

### When to Add "use client"
Add "use client" at the top of the file only when required.
Push "use client" as far down the tree as possible — keep parent components
as server components and only make leaf components client components.

### Server Component Patterns
- Data fetching belongs in server components (async/await directly)
- Database queries go directly in server components
- API keys used for server-side calls stay out of the browser

### Client Component Patterns
- Interactive forms (with validation state)
- Dropdown menus, modals, tooltips
- Charts and visualizations (most chart libs require the DOM)
- Components using third-party hooks

Getting this rule right prevents a common pattern: AI adds “use client” to every component because it is the safe choice. With explicit rules, it defaults to server components and adds “use client” only when justified.

TypeScript Configuration

Next.js projects need explicit TypeScript rules to avoid common AI mistakes:

## TypeScript Rules
- Strict mode enabled (tsconfig.json: "strict": true)
- No `any` type — use `unknown` when type is genuinely unknown
- No non-null assertions (!) unless absolutely necessary with a comment explaining why
- Use `satisfies` operator for typed objects with literal checking
- Component props: define as interface, not type (unless discriminated union needed)

## Component Type Patterns
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary" | "ghost";
  disabled?: boolean;
}

export function Button({ label, onClick, variant = "primary", disabled = false }: ButtonProps) {
  ...
}

## Type Imports
- Always use `import type` for type-only imports
- import type { User } from "@/types/user";
- import { createUser } from "@/lib/db";  // value import

## Path Aliases
- Use @/ for src/ root (configured in tsconfig.json)
- @/components, @/lib, @/types, @/hooks
- Never use ../../.. relative imports

Data Fetching Patterns

The App Router changed data fetching significantly. Rules need to reflect current patterns:

## Data Fetching (App Router)

### Server Components — Async/Await Directly
async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.products.findUnique({ where: { id: params.id } });
  if (!product) notFound();
  return <ProductDetail product={product} />;
}

### Server Actions — For Form Mutations
"use server";

export async function createProduct(formData: FormData) {
  const name = formData.get("name") as string;
  await db.products.create({ data: { name } });
  revalidatePath("/products");
}

### API Routes — Only When You Need an Actual API
// Use only for: webhooks, OAuth callbacks, third-party integrations
// Do NOT create API routes just to fetch data for server components

### Client-Side Fetching — For Interactive Updates
// Use SWR or React Query for data that updates without full navigation
// Use for: real-time feeds, polling, user-triggered refreshes

### Cache Behavior
// By default, fetch() is cached in App Router
// Add { cache: "no-store" } for dynamic data
// Add { next: { revalidate: 3600 } } for ISR behavior

Form Handling and Validation

## Forms

### Server Actions with Progressive Enhancement
// Prefer Server Actions for forms — they work without JavaScript
"use server";
import { z } from "zod";

const CreateUserSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
});

export async function createUser(prevState: unknown, formData: FormData) {
  const validated = CreateUserSchema.safeParse({
    name: formData.get("name"),
    email: formData.get("email"),
  });

  if (!validated.success) {
    return { error: validated.error.flatten() };
  }

  await db.users.create({ data: validated.data });
  redirect("/users");
}

### Validation
- Zod for schema validation (not yup, not joi)
- Validate on the server, not just the client
- Use react-hook-form only when complex client-side form state is needed

### Never
- Submit JSON to API routes for forms that could use Server Actions
- Validate only on the client
- Trust client-sent user IDs for authorization

Styling Rules

Styling preferences vary significantly, so be explicit:

## Styling
- Tailwind CSS for all styling (no CSS modules, no styled-components)
- shadcn/ui for UI components (in /components/ui/)
- Component variants with cva() (class-variance-authority)
- Responsive: mobile-first (sm:, md:, lg: prefixes)

## Tailwind Conventions
- Prefer utility classes over custom CSS
- For repeated patterns, create a component — not a @apply directive
- Dark mode: class-based (dark:), not media query
- No inline style={} unless absolutely necessary (animations, dynamic values)

## shadcn/ui Usage
// Import from @/components/ui/, never from the library directly
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";

// Extend with variants using cva
import { cva } from "class-variance-authority";

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

Testing Rules for Next.js

## Testing Setup
- Vitest for unit tests (not Jest — Vitest is faster with Vite/Next.js)
- @testing-library/react for component tests
- Playwright for E2E tests
- Test command: npx vitest run
- E2E command: npx playwright test

## What to Test
- Server Actions: test the function directly (they're just async functions)
- Server Components: test with React Testing Library or direct render
- Client Components: test interactivity with @testing-library/user-event
- API routes: test with a test HTTP client

## Example: Testing a Server Action
import { createUser } from "@/actions/users";
import { db } from "@/lib/db";
import { vi } from "vitest";

vi.mock("@/lib/db");

test("createUser creates a user and returns success", async () => {
  vi.mocked(db.users.create).mockResolvedValue({ id: "1", name: "Alice" });
  
  const formData = new FormData();
  formData.set("name", "Alice");
  formData.set("email", "[email protected]");
  
  const result = await createUser({}, formData);
  expect(result).toBeUndefined(); // redirect was called
  expect(db.users.create).toHaveBeenCalledWith({
    data: { name: "Alice", email: "[email protected]" }
  });
});

Project Structure

## File Structure (App Router)
src/
  app/                    # Next.js App Router
    layout.tsx            # Root layout
    page.tsx              # Home page
    [feature]/
      page.tsx            # Feature page (server component)
      loading.tsx         # Loading UI
      error.tsx           # Error UI
  components/
    ui/                   # shadcn/ui components (generated)
    [feature]/            # Feature-specific components
    shared/               # Shared across features
  lib/
    db.ts                 # Database client
    auth.ts               # Auth utilities
    utils.ts              # Shared utilities (cn, etc.)
  actions/                # Server Actions
  types/                  # TypeScript type definitions
  hooks/                  # Custom React hooks (client-side)

## Naming Conventions
- Pages: lowercase with dashes (app/user-settings/page.tsx)
- Components: PascalCase (UserProfile.tsx)
- Hooks: camelCase with "use" prefix (useUserProfile.ts)
- Server Actions: camelCase verbs (createUser, deleteProduct)
- Types: PascalCase interfaces (UserProfile, ProductWithVariants)

Complete CLAUDE.md Template for Next.js Projects

# Next.js Project — Claude Code Instructions

## Stack
- Next.js 15 (App Router)
- TypeScript strict mode
- Tailwind CSS + shadcn/ui
- Prisma + PostgreSQL
- Server Actions for mutations
- Vitest + Playwright for testing

## Component Rules
- Default to Server Components (no "use client")
- Add "use client" only when: hooks, browser APIs, or interactive event handlers needed
- Push client components down the tree

## Data Fetching
- Async/await directly in Server Components
- Server Actions for form mutations (not API routes)
- SWR for client-side real-time data

## TypeScript
- No `any` (use `unknown`)
- No non-null assertions without comment
- `import type` for type-only imports
- @/ path aliases

## Styling
- Tailwind utilities only (no CSS modules)
- shadcn/ui from @/components/ui/

## Testing
- Vitest: npx vitest run
- E2E: npx playwright test
- Test Server Actions directly

## Structure
- Pages: app/[feature]/page.tsx
- Components: src/components/[feature]/
- Server Actions: src/actions/
- Utilities: src/lib/

## Never
- "use client" on layout or page files without strong reason
- API routes for data that could be in a Server Component
- Mutable external state in Server Components
- Direct database calls in client components

For more React and Next.js specific AI rules, browse the Prompt Shelf gallery. For Python project rules, see our Python coding rules guide.

More from the blog

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules