Deep dive
Performance in Next.js is mostly about (1) **how much JS you ship**, and (2) **how much work you can cache**.
Practical improvements (more than two)
- **Reduce client JS**: keep the tree server-first, push `'use client'` down, avoid heavy client providers at the root.
- **Code-split heavy widgets**: dynamic imports for rarely used components.
- **Optimize images**: `next/image`, correct sizing, and sensible formats.
- **Cache and revalidate**: use cached `fetch` + `revalidate` instead of rendering everything per request.
- **Avoid waterfalls**: start fetches in parallel.
- **Fonts**: `next/font` helps reduce layout shifts.
Example (code splitting)
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('./Chart'), { ssr: false })
Common pitfalls
- Making a top-level layout a Client Component.
- Passing huge JSON payloads to the client.
- State too high in the client tree causing broad re-renders.