LESSON 7 ⏱️ 8 min read

Image Optimization

Why Image Optimization Matters

Images are often the largest assets on a page. Without optimization:

  • Slow page loads – Users wait for large images
  • Poor Core Web Vitals – LCP (Largest Contentful Paint) suffers
  • High bandwidth costs – Serving unoptimized images wastes resources

Next.js Image component provides:

  • Automatic format conversion (WebP, AVIF)
  • Lazy loading by default
  • Responsive srcset generation
  • Blur placeholder support

Configuring Next.js for WordPress Images

First, allow WordPress domain in next.config.js:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'your-wordpress-site.com',
        pathname: '/wp-content/uploads/**',
      },
      // Add localhost for development
      {
        protocol: 'http',
        hostname: 'localhost',
      },
    ],
  },
};

module.exports = nextConfig;
💡 Tip: Replace your-wordpress-site.com with your actual WordPress domain, or use an environment variable.

Creating a WordPress Image Component

Build a reusable component for WordPress images:

// src/components/WPImage.tsx
import Image from 'next/image';

interface WPImageProps {
  src: string;
  alt: string;
  width?: number;
  height?: number;
  fill?: boolean;
  priority?: boolean;
  className?: string;
  sizes?: string;
}

export function WPImage({
  src,
  alt,
  width,
  height,
  fill = false,
  priority = false,
  className = '',
  sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
}: WPImageProps) {
  // Handle relative URLs from WordPress
  const imageSrc = src.startsWith('http') 
    ? src 
    : `${process.env.NEXT_PUBLIC_WORDPRESS_URL}${src}`;
  
  if (fill) {
    return (
      <Image
        src={imageSrc}
        alt={alt}
        fill
        priority={priority}
        className={`object-cover ${className}`}
        sizes={sizes}
      />
    );
  }
  
  return (
    <Image
      src={imageSrc}
      alt={alt}
      width={width || 1200}
      height={height || 630}
      priority={priority}
      className={className}
      sizes={sizes}
    />
  );
}

Using in Post Cards

Update PostCard to use optimized images:

// src/components/PostCard.tsx
import { WPImage } from './WPImage';
import { WPPostWithEmbed, getFeaturedImageUrl } from '@/lib/types';

export function PostCard({ post }: { post: WPPostWithEmbed }) {
  const imageUrl = getFeaturedImageUrl(post, 'medium_large');
  
  return (
    <article className="border rounded-lg overflow-hidden">
      {imageUrl && (
        <div className="aspect-video relative">
          <WPImage
            src={imageUrl}
            alt={post.title.rendered}
            fill
            sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
          />
        </div>
      )}
      <div className="p-4">
        {/* Content */}
      </div>
    </article>
  );
}

Blur Placeholder for Better UX

For a nicer loading experience, add blur placeholders:

// src/components/WPImage.tsx
interface WPImageProps {
  // ... existing props
  blurDataURL?: string;
}

export function WPImage({
  // ... existing props
  blurDataURL,
}: WPImageProps) {
  // Generate a simple blur placeholder if not provided
  const placeholder = blurDataURL ? 'blur' : 'empty';
  
  return (
    <Image
      src={imageSrc}
      alt={alt}
      fill={fill}
      width={!fill ? width : undefined}
      height={!fill ? height : undefined}
      priority={priority}
      className={className}
      sizes={sizes}
      placeholder={placeholder}
      blurDataURL={blurDataURL}
    />
  );
}

Generating Blur Data URLs

For static images, you can generate blur placeholders at build time:

// src/lib/images.ts
import { getPlaiceholder } from 'plaiceholder';

export async function getBlurDataUrl(imageUrl: string): Promise<string | null> {
  try {
    const res = await fetch(imageUrl);
    const buffer = await res.arrayBuffer();
    const { base64 } = await getPlaiceholder(Buffer.from(buffer));
    return base64;
  } catch {
    return null;
  }
}

Install plaiceholder:

npm install plaiceholder sharp

Images in Post Content

WordPress content may include images. Handle them with custom components:

// src/components/PostContent.tsx
'use client';

import { useEffect, useRef } from 'react';

interface PostContentProps {
  content: string;
}

export function PostContent({ content }: PostContentProps) {
  const contentRef = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    if (!contentRef.current) return;
    
    // Find all images in content
    const images = contentRef.current.querySelectorAll('img');
    
    images.forEach((img) => {
      // Add lazy loading
      img.loading = 'lazy';
      
      // Add responsive classes
      img.classList.add('max-w-full', 'h-auto', 'rounded-lg');
      
      // Wrap in figure if not already
      if (img.parentElement?.tagName !== 'FIGURE') {
        const figure = document.createElement('figure');
        figure.className = 'my-6';
        img.parentElement?.insertBefore(figure, img);
        figure.appendChild(img);
      }
    });
  }, [content]);
  
  return (
    <div
      ref={contentRef}
      className="prose prose-lg max-w-none"
      dangerouslySetInnerHTML={{ __html: content }}
    />
  );
}

Performance Tips

1. Use Appropriate Sizes

// For hero images (full width)
sizes="100vw"

// For grid of 3 columns
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"

// For sidebar images
sizes="(max-width: 768px) 100vw, 300px"

2. Priority Loading for Above-the-Fold

// First post in grid - load immediately
<WPImage priority={index === 0} ... />

3. Use Next.js Image Optimization API

Images are automatically optimized through /_next/image. Configure quality:

// next.config.js
images: {
  formats: ['image/avif', 'image/webp'],
  minimumCacheTTL: 60 * 60 * 24, // 24 hours
}

Summary

In this lesson, we:

  • ✅ Configured Next.js for WordPress images
  • ✅ Created a reusable WPImage component
  • ✅ Added blur placeholders
  • ✅ Handled images in post content
  • ✅ Applied performance optimizations

Next up: SEO and metadata optimization.