--- name: image-optimization description: Image optimization with Next.js 15 Image, AVIF/WebP formats, blur placeholders, responsive sizes, and CDN loaders. Use when improving image performance, responsive sizing, or Next.js image pipelines. tags: [images, next-image, avif, webp, responsive, lazy-loading, blur-placeholder, lcp] context: fork agent: frontend-ui-developer version: 1.0.0 allowed-tools: [Read, Write, Grep, Glob] author: OrchestKit user-invocable: false --- # Image Optimization Production image optimization patterns for modern web applications. ## Overview - Optimizing Largest Contentful Paint (LCP) - Reducing page weight and bandwidth - Implementing responsive images - Adding blur placeholders for perceived performance - Converting to modern formats (AVIF, WebP) ## Core Patterns ### 1. Next.js Image Component ```tsx import Image from 'next/image'; // Static import (recommended for static assets) import heroImage from '@/public/hero.jpg'; function Hero() { return ( Hero banner ); } // Remote images Remote photo ``` ### 2. Responsive Images with Sizes ```tsx // Full-width hero Hero // Sidebar image (smaller on large screens) Sidebar // Grid of cards {product.name} ``` ### 3. Blur Placeholders ```tsx // Static imports get automatic blur import photo from '@/public/photo.jpg'; Photo // Remote images need blurDataURL Photo // Generate blurDataURL at build time import { getPlaiceholder } from 'plaiceholder'; export async function getStaticProps() { const { base64 } = await getPlaiceholder('/public/photo.jpg'); return { props: { blurDataURL: base64 } }; } ``` ### 4. Format Selection (AVIF/WebP) ```tsx // next.config.js - Enable AVIF module.exports = { images: { formats: ['image/avif', 'image/webp'], deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, }; // HTML picture element for non-Next.js Hero ``` ### 5. Lazy Loading Patterns ```tsx // Default: lazy loading (below the fold) Photo // Above the fold: eager loading Hero // Native lazy loading (non-Next.js) Photo ``` ### 6. Image CDN Configuration ```tsx // next.config.js - External image domains module.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 'cdn.example.com', pathname: '/images/**', }, { protocol: 'https', hostname: '*.cloudinary.com', }, ], }, }; // Cloudinary loader const cloudinaryLoader = ({ src, width, quality }) => { return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`; }; Cloudinary image ``` ### 7. Art Direction (Different Images per Breakpoint) ```tsx 'use client'; import Image from 'next/image'; import { useMediaQuery } from '@/hooks/useMediaQuery'; function ResponsiveHero() { const isMobile = useMediaQuery('(max-width: 768px)'); return ( Hero ); } // Or use CSS to swap
Hero Hero
``` ### 8. SVG and Icon Optimization ```tsx // Inline SVG for small icons (avoid network requests) import { IconCheck } from '@/components/icons'; // SVG sprites for many icons ... ... // Large decorative SVGs: use Image component ``` ## Performance Metrics Impact | Optimization | LCP Impact | CLS Impact | Bandwidth | |--------------|------------|------------|-----------| | AVIF format | -20-30% load | None | -50% size | | Responsive sizes | -30-50% load | None | -40% size | | Blur placeholder | Perceived faster | Prevents shift | +1kb | | Priority loading | -500ms+ | None | None | | Lazy loading | None (below fold) | None | Deferred | ## Anti-Patterns to Avoid | Anti-Pattern | Problem | Solution | |--------------|---------|----------| | No width/height | CLS from layout shift | Always set dimensions | | Eager load all | Slow initial load | Use lazy loading | | No priority on LCP | Slow LCP | Add priority prop | | PNG for photos | Large file size | Use AVIF/WebP | | Single image size | Wasted bandwidth | Use responsive sizes | ## Build-Time Optimization ```bash # Sharp for Next.js (auto-installed) npm install sharp # Squoosh CLI for batch optimization npx @squoosh/cli --webp '{"quality":80}' --avif '{"quality":65}' ./images/* ``` ## Quick Reference ```typescript // ✅ LCP Hero Image (static import for blur) import heroImage from '@/public/hero.jpg'; Hero // ✅ Remote image with explicit dimensions Photo // ✅ Responsive product card {product.name} // ✅ next.config.js for AVIF/WebP images: { formats: ['image/avif', 'image/webp'], remotePatterns: [{ hostname: 'cdn.example.com' }], } // ❌ NEVER: Missing dimensions (causes CLS) Photo // Missing width/height! // ❌ NEVER: Priority on non-LCP images // Wastes bandwidth // ❌ NEVER: Using PNG for photos // Use AVIF/WebP instead ``` ## Key Decisions | Decision | Option A | Option B | Recommendation | |----------|----------|----------|----------------| | Image format | JPEG/PNG | AVIF/WebP | **AVIF** (30-50% smaller), WebP fallback | | Next.js Image | Static import | Remote URL | **Static import** for automatic blur placeholder | | Lazy loading | Always lazy | Priority for LCP | **Priority for LCP**, lazy for rest | | Quality setting | 100 | 75-85 | **75-85** - imperceptible difference, much smaller | | Placeholder | None | Blur | **Blur** - better perceived performance | | Dimensions | Fill mode | Explicit w/h | **Fill** with aspect-ratio container for flexibility | ## Anti-Patterns (FORBIDDEN) ```typescript // ❌ FORBIDDEN: Missing width/height (causes CLS) Photo // ✅ CORRECT: Always set dimensions Photo // ❌ FORBIDDEN: Using fill without container sizing
Photo {/* No container size! */}
// ✅ CORRECT: Fill needs sized container
Photo
// ❌ FORBIDDEN: priority on all images {images.map(img => ( {img.alt} // All priority! ))} // ✅ CORRECT: Only LCP image gets priority {/* LCP only */} {belowFoldImages.map(img => ( {img.alt} /* Default lazy */ ))} // ❌ FORBIDDEN: No sizes prop on responsive images // No sizes = 100vw assumed always // ✅ CORRECT: Always specify sizes // ❌ FORBIDDEN: Using remote images without allowlist // Not in remotePatterns! // ✅ CORRECT: Configure remotePatterns in next.config.js // ❌ FORBIDDEN: PNG for photographs // PNG is for transparency, not photos // ✅ CORRECT: Use AVIF/WebP for photos // Next.js converts to AVIF/WebP // ❌ FORBIDDEN: Quality 100 // Huge file, no visual benefit // ✅ CORRECT: Quality 75-85 // ❌ FORBIDDEN: Loading LCP content via client-side fetch useEffect(() => { fetchHeroImage().then(setHero); // LCP waits for JS + fetch! }, []); // ✅ CORRECT: Server-render LCP images export default async function Page() { const hero = await getHero(); return ; } // ❌ FORBIDDEN: Empty alt on non-decorative images // Inaccessible! // ✅ CORRECT: Meaningful alt text Red sneakers, side view ``` ## Related Skills - `core-web-vitals` - LCP optimization, performance monitoring - `accessibility-specialist` - Image alt text, WCAG compliance - `react-server-components-framework` - Server-rendering for LCP images - `frontend-ui-developer` - Modern frontend patterns ## Capability Details ### next-image **Keywords**: next/image, Image component, fill, priority, sizes, quality **Solves**: Automatic optimization, format conversion, responsive images ### avif-webp **Keywords**: AVIF, WebP, format, compression, modern-formats **Solves**: Reducing image file size by 30-50% with same quality ### blur-placeholder **Keywords**: blur, placeholder, blurDataURL, plaiceholder, perceived-performance **Solves**: Better perceived performance, visual stability during load ### responsive-sizes **Keywords**: sizes, srcset, responsive, breakpoint, viewport **Solves**: Serving appropriately-sized images for each device ### image-cdn **Keywords**: CDN, Cloudinary, imgix, Cloudflare, loader, remote **Solves**: Global distribution, on-demand transformation, caching ### lazy-loading **Keywords**: lazy, loading, priority, eager, preload, LCP **Solves**: Reducing initial page load by deferring off-screen images ## References - `references/cdn-setup.md` - Image CDN configuration - `scripts/image-component.tsx` - Reusable image wrapper - `checklists/image-checklist.md` - Optimization checklist - `examples/image-examples.md` - Real-world image patterns