AgentSkillsCN

nextjs-best-practices

Next.js App Router 的核心原理。包括服务器组件、数据获取机制,以及路由模式。

SKILL.md
--- frontmatter
name: nextjs-best-practices
description: Next.js App Router principles. Server Components, data fetching, routing patterns.
allowed-tools: Read, Write, Edit, Glob, Grep

Next.js Best Practices

Principles for Next.js App Router development.


1. Server vs Client Components

Decision Tree

code
Does it need...?
│
├── useState, useEffect, event handlers
│   └── Client Component ('use client')
│
├── Direct data fetching, no interactivity
│   └── Server Component (default)
│
└── Both? 
    └── Split: Server parent + Client child

By Default

TypeUse
ServerData fetching, layout, static content
ClientForms, buttons, interactive UI

2. Data Fetching Patterns

Fetch Strategy

PatternUse
DefaultStatic (cached at build)
RevalidateISR (time-based refresh)
No-storeDynamic (every request)

Data Flow

SourcePattern
DatabaseServer Component fetch
APIfetch with caching
User inputClient state + server action

3. Routing Principles

File Conventions

FilePurpose
page.tsxRoute UI
layout.tsxShared layout
loading.tsxLoading state
error.tsxError boundary
not-found.tsx404 page

Route Organization

PatternUse
Route groups (name)Organize without URL
Parallel routes @slotMultiple same-level pages
Intercepting (.)Modal overlays

4. API Routes

Route Handlers

MethodUse
GETRead data
POSTCreate data
PUT/PATCHUpdate data
DELETERemove data

Best Practices

  • Validate input with Zod
  • Return proper status codes
  • Handle errors gracefully
  • Use Edge runtime when possible

5. Performance Principles

Image Optimization

  • Use next/image component
  • Set priority for above-fold
  • Provide blur placeholder
  • Use responsive sizes

Bundle Optimization

  • Dynamic imports for heavy components
  • Route-based code splitting (automatic)
  • Analyze with bundle analyzer

6. Metadata

Static vs Dynamic

TypeUse
Static exportFixed metadata
generateMetadataDynamic per-route

Essential Tags

  • title (50-60 chars)
  • description (150-160 chars)
  • Open Graph images
  • Canonical URL

7. Caching Strategy

Cache Layers

LayerControl
Requestfetch options
Datarevalidate/tags
Full routeroute config

Revalidation

MethodUse
Time-basedrevalidate: 60
On-demandrevalidatePath/Tag
No cacheno-store

8. Server Actions

Use Cases

  • Form submissions
  • Data mutations
  • Revalidation triggers

Best Practices

  • Mark with 'use server'
  • Validate all inputs
  • Return typed responses
  • Handle errors

9. Middleware & Security (Expert)

Middleware Chaining

Don't put everything in one file.

  • Login Logic -> middleware/auth.ts
  • Geo Logic -> middleware/geo.ts
  • Main middleware.ts combines them.

Security Headers

Must Have in next.config.js:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY (Prevent clickjacking)
  • Content-Security-Policy (Strict CSP prevents XSS)

10. Advanced Caching Rules

Tag-Based Revalidation

Instead of revalidating paths, rely on tags.

  1. Fetch: fetch(url, { next: { tags: ['products'] } })
  2. Action: revalidateTag('products') Result: Updates ALL product pages (list, detail, featured) at once.

"Stale-While-Revalidate"

Next.js does this by default for static pages.

  • User 1 visits: Sees cached version (FAST). Background background revalidation starts.
  • User 2 visits: Sees NEW version.

11. Testing Server Components

Mocking the Unmockable Since RSCs access DB directly, testing is hard.

  1. Integration Test (Best): Spin up DB, render component, check HTML.

  2. Unit Test (Mocking):

    typescript
    jest.mock('next/headers', () => ({
      cookies: () => ({ get: () => ({ value: 'token' }) })
    }));
    

12. Anti-Patterns

❌ Don't✅ Do
'use client' everywhereServer by default
Fetch in client componentsFetch in server
Skip loading statesUse loading.tsx
Ignore error boundariesUse error.tsx
Large client bundlesDynamic imports

10. Project Structure

code
app/
├── (marketing)/     # Route group
│   └── page.tsx
├── (dashboard)/
│   ├── layout.tsx   # Dashboard layout
│   └── page.tsx
├── api/
│   └── [resource]/
│       └── route.ts
└── components/
    └── ui/

Remember: Server Components are the default for a reason. Start there, add client only when needed.