perf hacks
AssetsImages

Image Optimization

Images are usually 40-60% of bandwidth. Here's how to fix that.

Key Insight

Images are typically the largest assets on a web page. Proper optimization can reduce page weight by 40-60% and significantly improve LCP. For cost analysis and CDN pricing, see the Cost Optimization Guide.

How Vercel Images Work

When you use next/image, Vercel:

  1. Transforms to requested size/format
  2. Caches at the edge
  3. Serves from cache on subsequent requests

You pay for:

  • Transformations: Each unique width × height × quality combo
  • Transfer: Optimized bytes sent to users

Using arbitrary widths like width={347} creates unique cached variants. 50 images with different widths = 50 transformations instead of 1.

The Standard Sizes Pattern

Define fixed sizes and stick to them:

image-sizes.ts
cloudinary-loader.ts
lib/image-sizes.ts
export const IMAGE_SIZES = {
  thumb: { width: 100, height: 100 },
  card: { width: 320, height: 180 },
  article: { width: 800, height: 450 },
  hero: { width: 1280, height: 720 },
} as const;

export type ImageSize = keyof typeof IMAGE_SIZES;
components/OptimizedImage.tsx
import Image from 'next/image';
import { IMAGE_SIZES, type ImageSize } from '@/lib/image-sizes';

export function OptimizedImage({ src, alt, size }: {
  src: string;
  alt: string;
  size: ImageSize;
}) {
  const { width, height } = IMAGE_SIZES[size];
  return <Image src={src} alt={alt} width={width} height={height} quality={80} />;
}

Now your app uses ~10 cached variants per image instead of dozens.

When to Use External CDN

Calculate break-even:

Monthly transformations × $0.05/1K = Vercel cost
Compare to: Cloudinary/Imgix plan

For 50K+ transformations/month, external often wins:

lib/cloudinary-loader.ts
export function cloudinaryLoader({ src, width, quality }: {
  src: string;
  width: number;
  quality?: number;
}) {
  const cloud = process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD;
  const q = quality || 80;
  return `https://res.cloudinary.com/${cloud}/image/fetch/w_${width},q_${q},f_auto/${encodeURIComponent(src)}`;
}
<Image loader={cloudinaryLoader} src={url} width={800} height={600} alt="Photo" />

Image Optimization Checklist

Use standard dimensions Avoid arbitrary values like width={347}. Stick to predefined sizes to maximize cache hits.

Set quality to 80 The default 75 is often too aggressive. 80 provides better quality with minimal size increase.

Add priority to LCP images Hero images and above-fold content should load immediately, not lazy load.

Evaluate external CDN for high volume At 50K+ transformations/month, Cloudinary or Imgix often beats Vercel's pricing.

Configure responsive sizes Tell the browser what size image to fetch based on viewport width.

Use blur placeholders Add placeholder="blur" with pre-generated blur data for better perceived performance.


Next: API Responses - the other big bandwidth opportunity.

On this page