Image Optimization
Rules
- Use
next/image for all images in Next.js — automatic optimization, lazy loading, responsive srcsets
- Always set
width and height or use fill with a sized container — prevents layout shift (CLS)
- Blur placeholder: generate
blurDataURL at build/upload time with plaiceholder or use placeholder="blur" for static imports
- Remote images: configure
remotePatterns in next.config.js — whitelist CDN domains
- Upload flow: client → presigned URL (S3/R2/Supabase Storage) → direct upload → store URL in database
- Resize on upload: use
sharp server-side to create multiple sizes (thumbnail, medium, full) — don't serve originals
- Formats: serve WebP/AVIF via
next/image auto-format — fallback to JPEG for compatibility
- CDN delivery: store originals in object storage (S3, R2), serve through CDN (CloudFront, Cloudflare) with cache headers
- Max file size validation: check on client (before upload) and server (before processing) — 5-10MB typical limit
Patterns
import Image from "next/image";
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={630}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQ..."
priority // above the fold
/>
<div className="relative aspect-video">
<Image src={url} alt={alt} fill className="object-cover" sizes="(max-width: 768px) 100vw, 50vw" />
</div>
import sharp from "sharp";
async function processUpload(buffer: Buffer) {
const thumbnail = await sharp(buffer).resize(200, 200, { fit: "cover" }).webp({ quality: 80 }).toBuffer();
const medium = await sharp(buffer).resize(800).webp({ quality: 85 }).toBuffer();
const full = await sharp(buffer).resize(1920).webp({ quality: 90 }).toBuffer();
return { thumbnail, medium, full };
}
Avoid
- Using
<img> tags in Next.js — lose automatic optimization, lazy loading, and format conversion
- Missing
sizes prop on responsive images — browser can't pick the right srcset size
- Serving unresized originals — a 5MB photo displayed at 400px is wasteful
- Skipping file type validation — check MIME type server-side, not just file extension