# Nextjs Best Practices

> Next.js App Router principles. Server Components, data fetching, routing patterns.

- Skill: `whd4/nextjs-best-practices` (Agent Skill)
- Install (CLI): `npx skillmds add whd4/nextjs-best-practices`
- Raw SKILL.md: https://api.skillmd.com/api/skills/whd4/nextjs-best-practices/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: whd4 (https://skillmd.com/u/whd4)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/whd4/nextjs-best-practices

---


# Next.js Best Practices

> Principles for Next.js App Router development.

---

## 1. Server vs Client Components

### Decision Tree

```
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

| Type | Use |
|------|-----|
| **Server** | Data fetching, layout, static content |
| **Client** | Forms, buttons, interactive UI |

---

## 2. Data Fetching Patterns

### Fetch Strategy

| Pattern | Use |
|---------|-----|
| **Default** | Static (cached at build) |
| **Revalidate** | ISR (time-based refresh) |
| **No-store** | Dynamic (every request) |

### Data Flow

| Source | Pattern |
|--------|---------|
| Database | Server Component fetch |
| API | fetch with caching |
| User input | Client state + server action |

---

## 3. Routing Principles

### File Conventions

| File | Purpose |
|------|---------|
| `page.tsx` | Route UI |
| `layout.tsx` | Shared layout |
| `loading.tsx` | Loading state |
| `error.tsx` | Error boundary |
| `not-found.tsx` | 404 page |

### Route Organization

| Pattern | Use |
|---------|-----|
| Route groups `(name)` | Organize without URL |
| Parallel routes `@slot` | Multiple same-level pages |
| Intercepting `(.)` | Modal overlays |

---

## 4. API Routes

### Route Handlers

| Method | Use |
|--------|-----|
| GET | Read data |
| POST | Create data |
| PUT/PATCH | Update data |
| DELETE | Remove 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

| Type | Use |
|------|-----|
| Static export | Fixed metadata |
| generateMetadata | Dynamic per-route |

### Essential Tags

- title (50-60 chars)
- description (150-160 chars)
- Open Graph images
- Canonical URL

---

## 7. Caching Strategy

### Cache Layers

| Layer | Control |
|-------|---------|
| Request | fetch options |
| Data | revalidate/tags |
| Full route | route config |

### Revalidation

| Method | Use |
|--------|-----|
| Time-based | `revalidate: 60` |
| On-demand | `revalidatePath/Tag` |
| No cache | `no-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' everywhere | Server by default |
| Fetch in client components | Fetch in server |
| Skip loading states | Use loading.tsx |
| Ignore error boundaries | Use error.tsx |
| Large client bundles | Dynamic imports |

---

## 10. Project Structure

```
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.

