Image optimization
Format, size, compression, lazy vs priority, reserved space and next/image.
Updated
Why images matter
On most sites images make up over half of the page weight and are usually the LCP element (the largest visible element). A single 4 MB photo straight from a phone can make a page slow no matter how good the rest of the code is.
Image optimization comes down to 5 decisions: format, size, compression, when it loads and how you reserve space.
1. The right format
| Format | For | Size vs JPEG |
|---|---|---|
| AVIF | photos, complex images | ~50% smaller |
| WebP | photos, illustrations, transparency | ~25–35% smaller |
| JPEG | photos (universal fallback) | baseline |
| PNG | screenshots with text, exact transparency | much larger for photos |
| SVG | logos, icons, vector illustrations | tiny, infinitely scalable |
| GIF | — | no — an MP4 / WebM video is 5–10× smaller |
2. The right size
Don't serve a 3000px image in a 300px card. The rule: the image is the displayed width × the screen density (usually 2×). With srcset + sizes (see Images and media), the browser picks the right variant itself.
3. Compression
Quality 75–85 is usually visually indistinguishable from 100 at half the size. For SVG, run SVGO (removes metadata, simplifies paths).
4. When it loads
| Image | What to do |
|---|---|
| the main image, visible immediately (LCP) | not lazy; fetchpriority="high" / priority in next/image |
| below the fold | loading="lazy" — loads only when it gets close |
| decorative backgrounds | CSS, possibly smaller / blurrier |
5. Reserve the space
width + height (or aspect-ratio in CSS) → the browser reserves the space before the image arrives → no CLS.
next/image — all of it, automatically
import Image from 'next/image'
import hero from '@/shared/assets/hero.jpg' // static import: dimensions + blur automatically
<Image src={hero} alt="The team at the office" priority placeholder="blur" sizes="100vw" />
<Image
src={product.imageUrl} // remote image
alt={product.name}
width={600}
height={600}
sizes="(min-width: 64rem) 33vw, 100vw"
/>What next/image does |
How |
|---|---|
| converts to AVIF / WebP | based on what the browser accepts |
| generates several sizes | automatic srcset; you only provide sizes |
| lazy by default | priority for the LCP image |
| prevents CLS | requires width/height, a static import or fill |
| placeholder | placeholder="blur" (automatic with static imports) |
| caching | optimized images are cached on the server / CDN |
| Prop | When |
|---|---|
sizes |
always when the image has no fixed width — without it the browser may download the largest variant |
fill |
the image fills a parent with position: relative and dimensions (cards, heroes) |
quality |
75 by default; you rarely need more |
images.remotePatterns in next.config.ts |
required for images from other domains (security) |
Where images live
| Source | Recommendation |
|---|---|
| project images (logo, illustrations) | in the repo, imported statically |
| user-uploaded images | storage (S3, Vercel Blob, Cloudinary) + next/image with remotePatterns |
| many images, transformations (crop, filters) | an image service / CDN (Cloudinary, imgix) with a custom loader |
Checklist
- modern format (AVIF / WebP), SVG for icons
- the right size +
sizes - the LCP image with
priority, the rest lazy -
width/height, orfillinside a sized parent - descriptive
altand a meaningful file name (also helps SEO) - no GIFs — a
muted autoplay loop playsinlinevideo instead
Summary
- Images usually decide LCP and page weight.
- AVIF/WebP, the right size, quality ~75–85, lazy below the fold,
priorityfor LCP, reserved dimensions. next/imagedoes almost everything — you providealt,sizesandpriority.