Next.js Image Optimization Cheat Sheet

Next.js Image Optimization Cheatsheet

Basic Image Component

Import and Basic Usage

import Image from 'next/image';

export default function MyComponent() {
    return (
        <Image
            src="/hero-image.jpg"
            alt="Hero image description"
            width={800}
            height={600}
            priority
        />
    );
}

External Images

// next.config.js
const nextConfig = {
    images: {
        remotePatterns: [
            {
                protocol: 'https',
                hostname: 'example.com',
                port: '',
                pathname: '/images/*',
            },
        ],
    },
};

// Component
<Image
    src="https://example.com/images/photo.jpg"
    alt="External image"
    width={500}
    height={300}
    placeholder="blur"
    blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
/>

Responsive Images

Fill Container

<div style={{ position: 'relative', width: '100%', height: '400px' }}>
    <Image
        src="/landscape.jpg"
        alt="Landscape"
        fill
        style={{ objectFit: 'cover' }}
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
    />
</div>

Responsive Sizes

<Image
    src="/product.jpg"
    alt="Product"
    width={500}
    height={300}
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
    style={{ width: '100%', height: 'auto' }}
/>

Multiple Breakpoints

<Image
    src="/banner.jpg"
    alt="Banner"
    width={1200}
    height={400}
    sizes="
        (max-width: 640px) 100vw,
        (max-width: 768px) 80vw,
        (max-width: 1024px) 60vw,
        50vw
    "
/>

Image Formats and Optimization

Modern Formats

// next.config.js
const nextConfig = {
    images: {
        formats: ['image/webp', 'image/avif'],
        deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
        imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    },
};

// Component automatically serves WebP/AVIF to supported browsers
<Image
    src="/photo.jpg"
    alt="Photo"
    width={800}
    height={600}
    quality={85}
/>

Quality Settings

// High quality for hero images
<Image
    src="/hero.jpg"
    alt="Hero"
    width={1200}
    height={600}
    quality={90}
    priority
/>

// Lower quality for thumbnails
<Image
    src="/thumbnail.jpg"
    alt="Thumbnail"
    width={150}
    height={150}
    quality={60}
/>

Loading Strategies

Priority Loading

// Above-the-fold images
<Image
    src="/hero-banner.jpg"
    alt="Hero banner"
    width={1200}
    height={400}
    priority // Preloads the image
/>

Lazy Loading

// Below-the-fold images (default behavior)
<Image
    src="/gallery-1.jpg"
    alt="Gallery image"
    width={400}
    height={300}
    loading="lazy" // Default, can be omitted
/>

Eager Loading

// Critical images that should load immediately
<Image
    src="/logo.png"
    alt="Logo"
    width={200}
    height={50}
    loading="eager"
/>

Placeholders and Loading States

Blur Placeholder

<Image
    src="/photo.jpg"
    alt="Photo"
    width={500}
    height={300}
    placeholder="blur"
    blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
/>

Color Placeholder

<Image
    src="/product.jpg"
    alt="Product"
    width={300}
    height={200}
    placeholder="blur"
    blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
    style={{ backgroundColor: '#f0f0f0' }}
/>

Loading Component

'use client';

import { useState } from 'react';
import Image from 'next/image';

export default function ImageWithLoader({ src, alt, ...props }) {
    const [isLoading, setIsLoading] = useState(true);
    
    return (
        <div style={{ position: 'relative' }}>
            {isLoading && (
                <div style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: '100%',
                    height: '100%',
                    backgroundColor: '#f0f0f0',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center'
                }}>
                    Loading...
                </div>
            )}
            <Image
                src={src}
                alt={alt}
                {...props}
                onLoad={() => setIsLoading(false)}
                style={{ opacity: isLoading ? 0 : 1, transition: 'opacity 0.3s' }}
            />
        </div>
    );
}

Object Fit and Positioning

Cover (Fill container, maintain aspect ratio)

<div style={{ position: 'relative', width: '300px', height: '200px' }}>
    <Image
        src="/portrait.jpg"
        alt="Portrait"
        fill
        style={{ objectFit: 'cover' }}
    />
</div>

Contain (Fit entire image, maintain aspect ratio)

<div style={{ position: 'relative', width: '300px', height: '200px' }}>
    <Image
        src="/landscape.jpg"
        alt="Landscape"
        fill
        style={{ objectFit: 'contain' }}
    />
</div>

Custom Positioning

<div style={{ position: 'relative', width: '400px', height: '300px' }}>
    <Image
        src="/photo.jpg"
        alt="Photo"
        fill
        style={{
            objectFit: 'cover',
            objectPosition: 'center top'
        }}
    />
</div>

Image Optimization Patterns

Gallery Grid

export default function ImageGallery({ images }) {
    return (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '1rem' }}>
            {images.map((image, index) => (
                <div key={image.id} style={{ position: 'relative', aspectRatio: '4/3' }}>
                    <Image
                        src={image.src}
                        alt={image.alt}
                        fill
                        style={{ objectFit: 'cover' }}
                        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                        loading={index < 3 ? 'eager' : 'lazy'}
                    />
                </div>
            ))}
        </div>
    );
}

Hero Section

export default function HeroSection() {
    return (
        <section style={{ position: 'relative', height: '60vh', minHeight: '400px' }}>
            <Image
                src="/hero-bg.jpg"
                alt="Hero background"
                fill
                priority
                style={{ objectFit: 'cover', objectPosition: 'center' }}
            />
            <div style={{
                position: 'relative',
                zIndex: 1,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                height: '100%',
                color: 'white'
            }}>
                <h1>Welcome to Our Site</h1>
            </div>
        </section>
    );
}

Product Card

export default function ProductCard({ product }) {
    return (
        <div style={{ border: '1px solid #eee', borderRadius: '8px', overflow: 'hidden' }}>
            <div style={{ position: 'relative', aspectRatio: '1' }}>
                <Image
                    src={product.image}
                    alt={product.name}
                    fill
                    style={{ objectFit: 'cover' }}
                    sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 25vw"
                />
            </div>
            <div style={{ padding: '1rem' }}>
                <h3>{product.name}</h3>
                <p>${product.price}</p>
            </div>
        </div>
    );
}

Performance Best Practices

Configuration Optimization

// next.config.js
const nextConfig = {
    images: {
        // Optimize for your use case
        deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
        imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
        
        // Enable modern formats
        formats: ['image/webp', 'image/avif'],
        
        // Configure domains for external images
        remotePatterns: [
            {
                protocol: 'https',
                hostname: 'example.com',
                port: '',
                pathname: '/images/*',
            },
        ],
        
        // Minimum cache time
        minimumCacheTTL: 60,
    },
};

Loading Strategy

// Prioritize above-the-fold images
export default function Page() {
    return (
        <div>
            {/* Hero image - load immediately */}
            <Image
                src="/hero.jpg"
                alt="Hero"
                width={1200}
                height={600}
                priority
            />
            
            {/* Content images - lazy load */}
            <div>
                <h2>Gallery</h2>
                {galleryImages.map((image, index) => (
                    <Image
                        key={image.id}
                        src={image.src}
                        alt={image.alt}
                        width={300}
                        height={200}
                        loading="lazy"
                        style={{ marginBottom: '1rem' }}
                    />
                ))}
            </div>
        </div>
    );
}

Error Handling

'use client';

import { useState } from 'react';
import Image from 'next/image';

export default function ImageWithFallback({ src, alt, fallbackSrc, ...props }) {
    const [imgSrc, setImgSrc] = useState(src);
    const [hasError, setHasError] = useState(false);
    
    return (
        <Image
            {...props}
            src={imgSrc}
            alt={alt}
            onError={() => {
                if (!hasError && fallbackSrc) {
                    setImgSrc(fallbackSrc);
                    setHasError(true);
                }
            }}
        />
    );
}

// Usage
<ImageWithFallback
    src="/primary-image.jpg"
    fallbackSrc="/fallback-image.jpg"
    alt="Product"
    width={300}
    height={200}
/>

Common Use Cases

Avatar Images

export default function Avatar({ src, alt, size = 40 }) {
    return (
        <div style={{
            position: 'relative',
            width: size,
            height: size,
            borderRadius: '50%',
            overflow: 'hidden'
        }}>
            <Image
                src={src || '/default-avatar.jpg'}
                alt={alt}
                fill
                style={{ objectFit: 'cover' }}
            />
        </div>
    );
}

Background Images

export default function BackgroundImage({ src, children }) {
    return (
        <div style={{ position: 'relative' }}>
            <Image
                src={src}
                alt=""
                fill
                style={{
                    objectFit: 'cover',
                    objectPosition: 'center',
                    zIndex: -1
                }}
            />
            <div style={{ position: 'relative', zIndex: 1 }}>
                {children}
            </div>
        </div>
    );
}

Responsive Logo

export default function Logo({ className }) {
    return (
        <Image
            src="/logo.svg"
            alt="Company Logo"
            width={150}
            height={50}
            style={{ width: 'auto', height: 'auto' }}
            className={className}
            priority
        />
    );
}