Rules: nextjs
Next.js Rules
Affected files
These rules apply to files matching the following patterns:
**/next.config.***/app/****/pages/****/middleware.ts**/middleware.js
Detailed rules
Next.js Rules
App Router (Next.js 13+)
- Use the App Router (
app/) unless migrating frompages/ - Special files:
page.tsx,layout.tsx,loading.tsx,error.tsx,not-found.tsx
Server vs Client Components
- By default: Server Component (RSC)
'use client': Client Component (interactivity) - push as low as possible'use server': Server Action (mutations)- RSCs CANNOT use hooks (useState, useEffect) or access the DOM
- Client Components CANNOT use
async/awaitdirectly
Data Fetching
| Pattern | When |
|---|---|
| Server Component fetch | Static/SSR data |
| Server Actions | Mutations (forms) |
| Route Handlers | API endpoints (app/api/route.ts) |
| Client fetch (SWR/Query) | Real-time data |
- Parallelize with
Promise.all()(avoid sequential cascades)
Caching and revalidation
| Method | Usage |
|---|---|
revalidatePath() | Invalidate a page after a mutation |
revalidateTag() | Invalidate by cache tag |
export const revalidate = 60 | ISR (periodic revalidation) |
Performance
next/imagefor all images,next/fontfor fonts,next/linkfor navigationloading.tsxfor Suspense boundariesdynamic()withssr: falsefor heavy components- Static metadata (
export const metadata) or dynamic (generateMetadata)
Streaming and Suspense
- Use
loading.tsxfor automatic per-route Suspense boundaries - Wrap async components in
<Suspense fallback={...}>for granular streaming - Progressive rendering pattern:
// page.tsx - The layout displays immediately, data streams in
export default function Page() {
return (
<main>
<Header /> {/* Rendered immediately */}
<Suspense fallback={<Skeleton />}>
<AsyncData /> {/* Streams when ready */}
</Suspense>
</main>
);
}
loading.tsx+<Suspense>= automatic streaming (no need forexport const dynamic)- Avoid cascades: parallelize fetches within the same Server Component
Server Components - Strict constraints
| Allowed in RSC | FORBIDDEN in RSC |
|---|---|
async/await | useState, useEffect, any hook |
| Direct DB access | Event handlers (onClick, onChange) |
| Filesystem access | Browser APIs (window, document) |
| Server-only imports | createContext |
- RSCs are always
asyncwhen they fetch data - Pass data as props to Client Components (not the other way around)
- Pattern: RSC fetch → interactive Client Component as a child
React 19 / Next.js 15+
forwardRefis no longer necessary:refis a direct propuseActionStatereplacesuseFormStateuse()to read promises/context in components<form action={serverAction}>for mutations
URL State Management
- Prefer
nuqsoruseSearchParamsfor state in the URL - Better for: filters, pagination, tabs, sorting
- Avoid
useStatefor state that should be shareable via URL
Anti-patterns
- DO NOT use
'use client'on pages/layouts unless absolutely necessary - DO NOT fetch in useEffect if a Server Component can provide the data
- DO NOT use
router.push()when a<Link>is enough - DO NOT use
getServerSideProps/getStaticPropswith App Router - DO NOT forget Suspense boundaries (causes bad LCP/CLS)
- DO NOT use
forwardRefin a React 19+ project
Automatic application
These rules are automatically applied by Claude during:
- Reading the matching files
- Modifying code
- Suggestions and fixes