billing hacks

Image Optimization

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

TL;DR

Use standard image dimensions (not arbitrary values like width={347}). Set quality to 80. Add priority to hero images. Consider external CDN at 50K+ transformations/month. For Core Web Vitals impact, see the Performance 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:

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" />

Key Takeaways

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

Set quality to 80 Better quality than default 75, minimal size increase.

Add priority to hero images LCP images should load immediately, not lazy load.

Evaluate external CDN at scale At 50K+ transformations/month, Cloudinary or Imgix often beats Vercel pricing.


Next: API Responses - the other big bandwidth opportunity.

For LCP optimization and responsive image techniques, see the Performance Guide: Image Optimization.

On this page