Performance is UX. Here’s a short list I actually use when a page “feels slow.”
Network
- Serve images in modern formats (WebP/AVIF). Use
next/image
. - Cache HTML with ISR (
revalidate
) for semi‑static pages. - Use HTTP caching headers for API responses (ETag/Cache‑Control).
- Defer non‑critical fetches until the component is visible (IntersectionObserver or suspense boundaries).
Rendering
- Memoize expensive components with
React.memo
and heavy values withuseMemo
. - Use key‑based list rendering; avoid re‑mounting large subtrees.
- Virtualize long lists (
react-window
/react-virtualized
). - Split code with dynamic imports (
next/dynamic
) for low‑priority UI.
Assets
- Remove unused icons and fonts. Subset fonts.
- Prefer CSS transforms/opacity for animations; avoid layout thrash.
- Inline tiny SVGs, lazy‑load large ones.
Perceived speed
- Show instant skeletons/spinners where data is not cached.
- Preload critical routes and hover‑prefetch links in Next.js.
- Keep interaction latency <100ms; debounce expensive handlers.
Quick snippet: route‑level code‑split
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
ssr: false,
loading: () => <div className="h-40 animate-pulse rounded bg-muted" />,
});
Apply a few of these and you’ll usually get the “this feels fast now” comment.