How to Compress Images for Next.js in 2026: App Router, Server Components, and Modern Formats
Complete guide to optimizing images for Next.js 15+ with App Router — use next/image for automatic optimization, ship AVIF and WebP with responsive srcset, implement blur placeholders, and prep assets locally before committing to your repo.

Next.js image optimization in 2026 is a partnership: next/image handles responsive delivery, format negotiation, and lazy loading, while you control source quality, dimensions, and format choice before assets reach the framework. Together they deliver fast LCP, minimal CLS, and smaller bundles — but only when you understand where Next.js helps and where manual prep still wins.
This guide covers the full pipeline for Next.js 15+ with App Router and Server Components — the default stack most teams are migrating to in late 2026. Everything here applies to Pages Router too, with routing-specific notes where the APIs differ.
What this guide covers
| Section | What you'll learn |
|---|---|
| Why compress for Next.js | What next/image does and doesn't do |
| The optimization split | Responsibilities: you handle source, Next.js handles delivery |
| Local prep workflow | Resize, format, and strip metadata before git |
| Using next/image | Props, layouts, priority, and placeholder patterns |
| Static vs dynamic images | Import vs URL, when each matters |
| AVIF and WebP strategy | How next/image picks formats and when to pre-convert |
| Blur placeholders | Base64, plaiceholder, and built-in blur generation |
| Custom loaders | Cloudinary, Imgix, and self-hosted origins |
| Build optimization | Sharp config, static imports, and output modes |
| Troubleshooting | CLS, slow builds, missing dimensions, format quirks |
Why compress for Next.js when the framework optimizes automatically
Next.js 15's next/image is powerful — it serves WebP and AVIF to capable browsers, generates responsive srcset, lazy-loads below-the-fold images, and caches optimized variants. That sounds like "upload anything and it fixes it."
Not quite. Here's what Next.js cannot fix:
- Oversized source dimensions — next/image resizes on request, but serving a 6000px original from origin before optimization wastes bandwidth on the first uncached load.
- Already-bloated uploads — if you commit a 4 MB camera JPEG, git stores 4 MB forever. Clones are slower, LFS bills are higher.
- Server load at scale — every new device width or format combination requires server-side encoding. Pre-compressed sources reduce CPU demand.
- Metadata exposure — EXIF GPS and timestamps pass through unless you strip them before commit.
The winning strategy: compress locally → commit clean assets → let next/image handle responsive delivery and format negotiation for each visitor.
The optimization split: you vs Next.js
Think of image optimization as a two-stage pipeline:
| Stage | Your responsibility | Next.js responsibility |
|---|---|---|
| Source prep | Resize to max display width × 2, export AVIF/WebP, strip metadata, commit small files | — |
| Delivery | — | Generate responsive srcset, negotiate format, lazy-load, cache variants |
You own the bytes before they hit the repo. Next.js owns the bytes served to each visitor.
What next/image does automatically
- Serves WebP and AVIF to browsers that support them (via
Acceptheader negotiation) - Generates device-optimized widths (srcset) based on
sizesprop - Lazy-loads images below the fold by default
- Applies blur placeholders when you provide
placeholder="blur" - Reserves layout space when you set
widthandheight(prevents CLS)
What you must do manually
- Resize originals to reasonable max dimensions (e.g., 1920–2400px wide for heroes)
- Export to efficient formats before commit (AVIF, WebP, or high-quality JPEG)
- Strip EXIF metadata from public assets
- Set correct
width,height, andsizesprops for every image - Mark LCP images with
priorityto prevent lazy-load delays
Local prep workflow: compress before commit
Before any image enters your Next.js project, run this checklist:
1. Resize to display width × 2
Most marketing heroes display at 1200–1600 CSS pixels wide. Export at 2400–3200px max to cover retina displays. Do not commit 6000px camera originals.
Open the batch image compressor, set max dimension to 2400px, and export.
2. Choose the right format
| Asset type | Best source format for Next.js |
|---|---|
| Hero photography | AVIF or WebP (next/image will optimize further) |
| Blog inline images | WebP or JPEG |
| UI screenshots | PNG or lossless WebP |
| Logos with transparency | PNG or SVG (use SVG when possible) |
| Product photos | WebP or AVIF |
For maximum compatibility, you can commit high-quality JPEGs and let next/image convert to WebP/AVIF on first request. For best performance, commit AVIF or WebP directly — next/image passes them through or optimizes minimally.
3. Strip metadata
Enable Strip metadata in Asset Melt's studio settings. Re-encoding removes EXIF GPS coordinates, device info, and timestamps before commit.
See privacy-first image compression for why metadata stripping matters for public repos.
4. Commit to public/ or import statically
/public/images/hero.avif
/public/images/hero-mobile.avif
Or import directly in components:
import heroImage from '@/public/images/hero.avif'
Smaller committed files = faster clones, cheaper LFS, and less origin bandwidth when next/image serves cached variants.
Using next/image correctly
Basic usage with static import
import Image from 'next/image'
import heroSrc from '@/public/images/hero.avif'
export default function HeroSection() {
return (
<Image
src={heroSrc}
alt="Asset Melt studio compressing images locally"
priority // Above fold — prevents lazy load
placeholder="blur"
sizes="(min-width: 1024px) 1200px, 100vw"
/>
)
}
Key props:
src— imported object or URL stringalt— required, descriptive alt textpriority— use on LCP image only; prevents lazy-load delayplaceholder="blur"— shows low-quality placeholder during load (requires static import or manual blurDataURL)sizes— tells browser which srcset width to fetch; critical for correct responsive delivery
Width and height (preventing CLS)
Always provide dimensions:
<Image
src="/images/product.webp"
alt="Oak dining table"
width={1200}
height={800}
sizes="(min-width: 768px) 50vw, 100vw"
/>
Or use fill for CSS-sized containers:
<div className="relative aspect-video">
<Image
src="/images/background.avif"
alt=""
fill
className="object-cover"
sizes="100vw"
/>
</div>
With fill, the container must have position: relative and an explicit height or aspect-ratio.
Static vs dynamic images
Static imports (recommended for known assets)
import hero from '@/public/hero.avif'
<Image src={hero} alt="Hero" priority />
Pros:
- Next.js reads dimensions and generates blur placeholders automatically
- Type-safe
- Bundler warns if file is missing
Cons:
- Cannot use for dynamic URLs from CMS or user uploads
Dynamic URLs (for CMS, user content)
<Image
src={post.heroUrl}
alt={post.heroAlt}
width={1200}
height={630}
sizes="100vw"
/>
Pros:
- Works with CMS, databases, external CDNs
Cons:
- You must manually provide width and height
- Blur placeholders require manual
blurDataURLprop - Requires
remotePatternsconfig in next.config.js
Configure allowed domains:
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/images/**',
},
],
},
}
AVIF and WebP strategy in Next.js
Next.js negotiates formats automatically via the Accept header. When a browser requests an image:
- Next.js checks if the visitor supports AVIF (via
Accept: image/avif) - If not, checks WebP support
- Falls back to original format (JPEG, PNG)
Should you pre-convert to AVIF before commit?
Yes when:
- The image is a hero or LCP candidate
- You want to minimize origin bandwidth
- Build performance matters (less server-side encoding)
Let Next.js convert when:
- You're migrating existing JPEG assets gradually
- The CMS only accepts JPEG uploads
- You need one source format for simplicity
Example: commit both AVIF and WebP sources
/public/images/
hero.avif (120 KB)
hero.webp (180 KB)
hero.jpg (500 KB, fallback only)
Use a picture element with next/image sources:
<picture>
<source srcSet="/images/hero.avif" type="image/avif" />
<source srcSet="/images/hero.webp" type="image/webp" />
<Image
src="/images/hero.jpg"
alt="Description"
width={1920}
height={1080}
priority
/>
</picture>
Or let next/image pick automatically by committing only AVIF — it will convert to WebP and JPEG on demand.
Read AVIF vs WebP for format tradeoffs.
Blur placeholders and LQIP
Next.js supports low-quality image placeholders (LQIP) to prevent blank space during image load.
Automatic blur with static imports
import hero from '@/public/hero.avif'
<Image src={hero} alt="Hero" placeholder="blur" />
Next.js generates a tiny base64-encoded thumbnail at build time.
Manual blur for dynamic URLs
Generate a blurDataURL with plaiceholder or similar:
<Image
src={post.heroUrl}
alt={post.heroAlt}
width={1200}
height={630}
placeholder="blur"
blurDataURL={post.blurDataURL}
/>
Store blurDataURL in your CMS or generate it server-side.
Empty placeholder for decorative images
<Image
src="/images/background.avif"
alt=""
fill
placeholder="empty"
className="object-cover -z-10"
/>
Use placeholder="empty" when the image is decorative and blur would confuse layout.
Custom loaders for external CDNs
If you host images on Cloudinary, Imgix, or a custom CDN, configure a loader:
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './lib/image-loader.js',
},
}
// lib/image-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
return `https://res.cloudinary.com/your-cloud/image/upload/${params.join(',')}/${src}`
}
Even with custom loaders, pre-compress originals before uploading to the CDN origin. Cloudinary and Imgix optimize on the fly, but they perform best when source files are already clean.
Optimizing at build time
Sharp configuration
Next.js uses sharp for server-side image optimization. Tune quality in next.config.js:
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
}
Key options:
formats— order matters; AVIF first, WebP seconddeviceSizes— breakpoints for full-width imagesimageSizes— fixed sizes for constrained images (thumbnails, avatars)minimumCacheTTL— how long optimized images are cached
Static export mode
When using output: 'export' for static hosting, next/image optimization is disabled. Pre-compress all images before build and use regular <img> tags or a custom image component.
Common Next.js image issues
CLS (Cumulative Layout Shift)
Problem: Image loads and pushes content down.
Fix: Always set width and height, or use fill with a container aspect-ratio.
<Image src={src} alt="Alt" width={1200} height={800} />
Slow local development
Problem: First image load is slow in dev mode.
Cause: Next.js optimizes images on demand. The first request encodes; subsequent requests serve from cache.
Fix: Accept dev mode slowness, or pre-compress aggressively so next/image passes through with minimal processing.
Missing remotePatterns error
Problem: Error: Invalid src prop ... hostname not configured
Fix: Add the domain to next.config.js:
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example.com' },
],
}
AVIF not serving
Problem: Browser supports AVIF but receives WebP or JPEG.
Cause: Check formats in next.config.js — AVIF must come before WebP.
Fix:
images: {
formats: ['image/avif', 'image/webp'],
}
LCP image lazy-loaded
Problem: Hero image has loading="lazy" even though it's above fold.
Fix: Add priority prop:
<Image src={hero} alt="Hero" priority />
priority sets loading="eager" and adds a preload link in <head>.
Real-world workflow
1. Export from design tool
Export at 2400px max width, AVIF or WebP.
2. Compress locally
Drop files into Asset Melt, enable metadata stripping, export to public/images/.
3. Commit
git add public/images/hero.avif
git commit -m "feat: add hero image for Q4 campaign"
4. Use in component
import Image from 'next/image'
import hero from '@/public/images/hero.avif'
export default function Hero() {
return (
<section className="relative h-screen">
<Image
src={hero}
alt="Q4 campaign hero: woman using laptop in cafe"
fill
priority
className="object-cover"
sizes="100vw"
/>
</section>
)
}
5. Verify
Run Lighthouse on mobile, check LCP element. If it's the hero image and LCP is under 2.5s on 4G, you're good.
Next.js vs WordPress vs Shopify compression
| Platform | Optimization approach | Your prep step |
|---|---|---|
| Next.js | Server-side on demand; caches variants | Resize and format before commit |
| WordPress | Plugin-based; varies by CDN | Compress before media library upload — see compress for WordPress |
| Shopify | Shopify CDN auto-resizes; preserves originals | Export at 2048px max, WebP or JPEG — see compress for Shopify |
Next.js gives you the most control. WordPress and Shopify optimize post-upload, so local prep before upload is even more critical there.
Server Components and image optimization
Next.js App Router Server Components fetch data on the server and render once. Images in Server Components work identically to Client Components:
// app/blog/[slug]/page.tsx (Server Component)
import Image from 'next/image'
export default async function BlogPost({ params }) {
const post = await fetchPost(params.slug)
return (
<article>
<Image
src={post.heroUrl}
alt={post.heroAlt}
width={1200}
height={630}
priority
/>
<h1>{post.title}</h1>
{/* ... */}
</article>
)
}
No special handling required. next/image optimizes regardless of component type.
Getting started today
- Audit
public/and find images over 500 KB. - Re-export at 1920–2400px max width as AVIF or WebP.
- Commit the smaller files and delete originals.
- Add
priorityto your LCP image component. - Verify with Lighthouse that LCP improved.
Related reading: Compress images without losing quality · AVIF vs WebP · Privacy-first compression · Optimize for Core Web Vitals
Frequently asked questions
Related tools
Continue in this topic
Pillar guideHow to Compress Images Without Losing Quality
Compressing images without losing quality is really about removing bytes people cannot see . Camera originals include oversized dimensions, metadata, and more…
Ready to compress images without uploading them?
Open Asset Melt Studio