Images, Performance & Optimization
next/image — Use It for Every Image
The Image component resizes, converts to WebP/AVIF, lazy-loads, and reserves space (no layout shift) automatically:
import Image from "next/image";
import hero from "@/public/hero.png"; // static import — dimensions known
<Image src={hero} alt="Dashboard preview" priority />
// remote images need explicit dimensions…
<Image
src={course.thumbnail}
alt={course.title}
width={640}
height={360}
/>
// …or fill a sized container (thumbnails, covers)
<div style={{ position: "relative", aspectRatio: "16/9" }}>
<Image src={course.thumbnail} alt="" fill style={{ objectFit: "cover" }} />
</div>Rules: priority on the above-the-fold hero (preloads it — biggest LCP win), accurate sizes for responsive grids, and remote hosts whitelisted in config:
// next.config.mjs
export default {
images: {
remotePatterns: [{ protocol: "https", hostname: "i.ytimg.com" }],
},
};Ship Less JavaScript
- Server components by default — every component without
"use client"is free. - Push “use client” to the leaves — a client button, not a client page.
- Dynamic-import heavy widgets:
import dynamic from "next/dynamic";
const Chart = dynamic(() => import("@/components/Chart"), {
loading: () => <Skeleton />,
ssr: false, // browser-only libs (canvas, maps)
});Find what’s heavy with the analyzer:
npm install @next/bundle-analyzer
ANALYZE=true npm run build # opens a treemap of every chunkStream the Slow Parts
Don’t let one slow query block the page — Suspense boundaries (data-fetching topic) plus loading.js give instant shells with content streaming in. Audit any page that “feels slow”: usually one await is serialising everything.
Core Web Vitals — What to Watch
- LCP (largest contentful paint, < 2.5s) — fixed by priority images, streaming, static rendering.
- CLS (layout shift, < 0.1) — fixed by next/image dimensions and next/font.
- INP (interaction latency, < 200ms) — fixed by shipping less JS and avoiding giant client components.
Measure with Lighthouse (DevTools), npm run build‘s route-size table, and Vercel Analytics in production. The framework defaults already optimise hard — most regressions come from accidental "use client" sprawl and unoptimised images, both now visible to you.