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
| 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.tscombines 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.
- •Fetch:
fetch(url, { next: { tags: ['products'] } }) - •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.
- •
Integration Test (Best): Spin up DB, render component, check HTML.
- •
Unit Test (Mocking):
typescriptjest.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
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.