CONTACT US
TABLE OF CONTENTS

Next.js Image Component: Performance and CWV in Practice

Text reads Next.js Image Component: Performance and CWV in Practice with abstract blue outlines of rectangles and a computer monitor on a dark background.

TL;DR — Key Takeaways

  • next/image does not guarantee good Core Web Vitals by default — layout and usage decisions matter far more.
  • Most LCP wins come from correct preloading (not over-preloading) of true hero images; everything else hurts.
  • CLS almost always stems from layout mistakes (missing dimensions, unconstrained fill, reflowing containers), not from the image component itself.
  • fill is convenient but frequently misapplied — it’s one of the top sources of production CLS regressions.
  • Preloading too many images (especially carousels or below-fold assets) competes with critical CSS/JS and can worsen First Contentful Paint (FCP).
  • next/image is unnecessary — and sometimes harmful — for icons, SVGs, tiny thumbnails, and animated assets.
  • In Next.js 16+, priority is no longer recommended in most cases: use preload, loading="eager", or fetchPriority="high" intentionally instead (see the official docs).

The next/image component was designed to fix most of these issues automatically — but in production, especially after Next.js 16 (released October 2025), many teams still see red CWV scores despite “using next/image correctly.” Why? Because the component is a powerful tool, not a silver bullet. Misuse (or over-reliance) creates regressions that vanilla <img> would have avoided.

We’ll cover benchmarks, anti-patterns, migration traps, and hard rules you can copy-paste into PR reviews.

What next/image Actually Does — and What It Doesn’t

Let’s cut through the hype.

What it does automatically (and very well):

  • On-demand resizing for device/viewport (no manual srcset hell)
  • Format negotiation: serves AVIF/WebP when supported, falls back gracefully
  • Lazy loading by default (images only load when approaching viewport)
  • Preloading support via the new preload prop (replacing the deprecated priority)

What it absolutely does NOT do:

  • Automatically prevent CLS — that requires correct width, height, aspect-ratio preservation, and container constraints.
  • Choose the right loading strategy for your page — preloading everything is a classic anti-pattern.
  • Fix bad architectural decisions (e.g., 50+ images decoding at once in a feed → INP killer).
  • Replace thoughtful layout design — fill without a positioned parent is still going to shift your page.

If you’re still getting layout shifts after adding next/image everywhere, the problem is almost never the component — it’s how you’re using it (a common theme in real-world production debugging, far beyond what older guides like those from mid-2025 cover).

How next/image Really Affects Core Web Vitals

Most teams add next/image everywhere, run Lighthouse once, see green scores in dev… then deploy and watch real-user CWV tank in production. Why? Because the component optimizes bytes and delivery — not your layout decisions, not your loading strategy, and not your architecture.

Here’s the honest breakdown of how it impacts the three Core Web Vitals in 2026 (post-Next.js 16), with the failure modes we see constantly in client projects.

Want to boost your website performance?

LCP — When Preloading Helps and When It Backfires

Largest Contentful Paint measures when the main content becomes visible. In many apps, that’s a hero image.

When preloading helps:
Use preload={true} (the explicit replacement for the now-deprecated priority prop) only on the single, true LCP candidate — typically one above-the-fold hero image. This inserts a <link rel="preload"> early in <head>, telling the browser to fetch it ASAP. Combine with loading="eager" or fetchPriority="high" for maximum effect (see the official recommendation).

A website audit report lists performance issues with estimated potential savings, such as reducing unused CSS and JavaScript, and improving image delivery, under sections for Issues and Diagnostics.

When it backfires (common production pitfalls):

  • Preloading multiple images (e.g., a row of product cards or carousel slides) → competes with critical CSS/JS → delays FCP and can actually worsen LCP.
  • Preloading below-the-fold images → wastes bandwidth on assets users might never see.
  • Using preload together with loading or fetchPriority → browsers ignore redundant hints, but you still pay the mental overhead.

Rule of thumb (copy-paste for PR reviews):

Preload exactly one image per page — the confirmed LCP hero.
Never preload carousels, grids, or anything below the first viewport.
If you have multiple potential LCP candidates (mobile vs desktop), let the browser decide — don’t force it.

In our benchmarks, correct single-hero preloading shaves 300–800 ms off LCP. Over-preloading adds 400–1200 ms of delay.

CLS — Why It Still Happens in “Correct” Implementations

Cumulative Layout Shift tracks unexpected movement after initial render. next/image helps… but only if you give it the tools.

How it helps:
When you supply accurate width and height (or use fill with a constrained parent), the component reserves space via aspect ratio. Modern browsers use this to prevent shifts (detailed in the Image component docs on CLS prevention).

Accessibility report showing three warnings: buttons and links do not have accessible or discernible names, and background and foreground colours do not have sufficient contrast ratio. Explanations are provided below each warning.

Why CLS still kills scores in production:

  • Missing or wrong dimensions → browser treats image as 0×0 until load → big shift when it appears.
  • fill without position: relative (or fixed/absolute) on the parent → image doesn’t render or overflows → layout breaks.
  • Responsive layouts that reflow containers after image load (e.g., dynamic padding/margins based on content).
  • Using aspectRatio prop incorrectly or relying on older browsers that ignore it (pre-Safari 15).

Rule of thumb:

Always set explicit width/height for static imports.
For fill, wrap in a div with position: relative and defined dimensions/aspect-ratio.
Test CLS in DevTools “Performance” tab with “Enable advanced paint instrumentation” — shifts jump out immediately.

We’ve seen teams drop CLS from 0.25+ to <0.05 just by fixing unconstrained fill usage.

INP — The Overlooked Cost of Too Many Images

Interaction to Next Paint (the 2024+ replacement for FID) measures responsiveness to user input. Images don’t directly cause INP… until they do.

A website performance report shows the Core Web Vitals assessment as failed. Key metrics: LCP 2.7s, INP 130ms, CLS 0.02. FCP is 2.1s and TTFB is 1.1s. Mobile is selected and various test info is shown.

Hidden INP killers tied to images:

  • Too many images decoding simultaneously (e.g., infinite scroll feed loading 20+ at once) → main-thread blocking → delayed clicks/taps.
  • Large images without decoding="async" (Next.js defaults to async for most cases) → decode work blocks event handlers.
  • Client-side layout thrashing when images load and trigger reflows (especially with fill in dynamic grids).
  • Over-optimized but numerous placeholders/blurs that still trigger paints on interaction.

Rule of thumb:

Limit visible images per viewport to ~8–12 max.
Use decoding="async" manually on large below-fold images if needed (though Next.js handles most cases).
Profile INP in Chrome DevTools → look for long “Recalculate Style” or “Layout” tasks triggered by image loads.

This is the vitals metric most teams ignore until users complain about “laggy” interactions — then blame React instead of asset volume.

We’ve fixed production INP regressions multiple times just by aggressive lazy-loading + capping image density.

[Next: See these failure modes in a real app → watch our video walkthrough below.]

Real Production Walkthrough: Fixing CWV in a Next.js App
We took a production Next.js dashboard where next/image was “used correctly” — yet LCP was 3.8s, CLS 0.18, and INP spiked on interactions.

Watch how subtle misuses (over-preloading, unconstrained fill, decode pressure) tanked scores… and the 5-minute fixes that brought it to green across the board.

[Embed your YouTube video here: https://www.youtube.com/watch?v=gAFLL1ZxrF4]

(Video timestamp suggestions: 0:45 – LCP preload mistake, 2:10 – CLS fill regression, 4:20 – INP decode thrashing fix.)

If your metrics still look off after applying these rules, the deeper patterns (and production debugging steps) are covered in our free ebook — link below in the next section.

Book a free consultation to startt your project optimisation

next/image vs : Real Benchmarks and Trade-offs

The myth is that you should always replace every <img> with next/image.
The reality in production apps (especially large-scale ones) is more nuanced.

next/image gives you powerful automatic optimizations — but it also adds runtime overhead: component hydration, wrapper divs, inline styles for placeholders, and server-side image resolution logic. For tiny assets or static icons, that overhead can outweigh the benefits.

Here’s a no-BS comparison based on real production patterns we’ve measured across multiple Next.js 14–16 apps.

ScenarioNative next/imageBetter Choice in 2026Why
Hero / LCP-critical imageManual srcset, sizes, loading=”eager”, preloadAutomatic resizing + AVIF/WebP + preload={true}next/image30–70% file size reduction + better format negotiation
Above-the-fold static logo / bannerSimple, no hydration costAdds wrapper + placeholder logicnext/image (if remote) or (if local) wins if asset is tiny/static and already optimized
SVG icon / inline graphicZero overhead, perfect sharpnessForces raster optimization (unless unoptimized) or inline SVGnext/image rasterizes SVGs → loses vector benefits + adds bloat
CMS thumbnails in a grid/list (50+ images)Fast client render, but manual lazy + srcsetAutomatic lazy + blur-up + responsive sizesnext/image (with unoptimized for tiny thumbs)Automation saves dev time; manual becomes maintenance hell
Very small decorative image (<5 KB)Instant, no JS costStill creates component + placeholder with loading=”lazy”Overhead of next/image is measurable on low-end devices
Animated GIF / WebP animationWorks nativelyCan break animation unless unoptimized or video elementnext/image often forces static fallback

Key takeaways from benchmarks we’ve run:

  • For LCP heroes → next/image consistently wins (faster delivery, smaller payloads).
  • For icons, SVGs, tiny assets → native <img> (or better: inline SVG) is lighter and faster to interactive.
  • In lists/feeds with 50+ images → next/image’s lazy + format negotiation usually nets 40–60% bandwidth savings, but only if you avoid over-optimizing thumbnails (use unoptimized for <10 KB images).
  • Hydration cost of next/image wrappers is small (~1–3 KB gzipped JS per image in bundles), but it adds up in image-heavy dashboards → profile with React DevTools.

Bottom line: Treat next/image as the default for content images, but deliberately fall back to for icons, logos, SVGs, and micro-assets. Blind replacement everywhere is one of the most common performance anti-patterns we audit.

Image Sizing That Doesn’t Break CWV

The single biggest reason teams still see CLS after adopting next/image is incorrect or missing sizing information.
The component can’t guess your layout — it relies on you to tell it how much space to reserve .

In 2026 (Next.js 16+), the rules haven’t changed fundamentally, but the cost of getting them wrong has gone up: higher INP penalties from reflows, stricter Lighthouse scoring, and users on slower networks noticing shifts more.

Here’s how to do sizing correctly — and the most common ways teams still break it.

Static Imports vs Remote Images

Static imports (local files in /public or next to the component) are the gold standard for performance and safety.

// Good: static import → Next.js knows exact dimensions at build time 
import hero from '@/public/hero-desktop.jpg';

Next.js extracts width/height automatically → perfect aspect ratio → zero CLS risk.

Remote images (from CMS, S3, external CDN) require explicit width/height or a trusted remotePatterns config .

// Acceptable but requires manual dimensions
<Image
  src="https://images.unsplash.com/photo-..."
  alt="Remote hero"
  width={1200}
  height={800}
  sizes="(max-width: 768px) 100vw, 50vw"
  className="object-cover"
/>

Anti-pattern to avoid: Omitting width/height on remote images → fallback to 0×0 → massive CLS when the image loads.

sizes Explained Without Guesswork

The sizes prop tells the browser which image variant to request before it knows the final layout . Wrong sizes → either oversized images (slow LCP) or under-optimized ones (blurry on desktop).

Correct pattern for a responsive hero that takes full width on mobile, half on desktop:

sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px"

Breakdown:

  • Mobile (<768px): 100vw → full viewport width
  • Tablet (768–1200px): 50vw → half viewport
  • Desktop (>1200px): fixed 800px max (prevents requesting 4K images on big screens)

Common mistake: Using only 100vw everywhere → Desktop requests unnecessarily huge images → slower LCP + higher bandwidth cost.

Rule of thumb for sizes:

  • Start with mobile-first: 100vw for full-width elements
  • Add media-query breakpoints that match your CSS
  • Cap at a realistic max (e.g., 1200px or 1600px) for large screens
  • Test with DevTools → Network tab → throttle to “Slow 4G” and check requested image sizes

fill: When It’s Right — and When It’s Wrong

fill is great for responsive, background-style images… but it’s the #1 CLS culprit when misused .

Correct usage (contained):

<div className="relative w-full pt-[56.25%]"> {/* 16:9 aspect ratio – preserves ratio to prevent shifts */}
  <Image
    src={hero}
    alt="Background video cover"
    fill
    sizes="(max-width: 768px) 100vw, 50vw"
    className="object-cover"
  />
</div>
  • Parent has position: relative
  • Aspect ratio is reserved via padding hack or aspect-ratio CSS
  • No layout shift

Wrong usage (causes CLS):

// BAD: no constrained parent
<div>
  <Image src={hero} alt="Hero" fill className="object-cover" />
</div>

→ Image has no bounding box → overflows or shifts content when it loads.

Anti-patterns we see constantly:

  • fill inside flex/grid items without fixed height → reflow on load
  • fill + object-fit: contain without aspect-ratio → white space shifts
  • fill on decorative background images that could be CSS background-image

Rule of thumb: Only use fill when you truly need it to fill a container responsively. Prefer explicit width/height + sizes for most content images — less magic, fewer surprises.

Mastering sizing is 70% of winning at next/image. Get this wrong, and no amount of quality tweaks or format negotiation will save your CWV scores.

Wrong (CLS disaster) usage:

// BAD – no space reserved
<div className="w-full">
  <Image src={hero} alt="Hero" fill className="object-cover" />
</div>

→ Image loads → content jumps down.

Anti-patterns we still see weekly:

  • fill inside auto-height flex/grid items → reflow cascade
  • fill + object-fit: contain without aspect ratio → letterboxing shifts
  • fill on small decorative elements that should be CSS backgrounds

Rule of thumb: Use fill only when the image truly needs to fill a responsive container. Prefer fixed width/height + sizes for 80% of content images — far fewer surprises.

Mastering sizing (not quality or formats) is what actually moves CWV needles in production. Get this wrong and the rest is cosmetic.

Advanced Patterns for Production Apps

Once the basics (sizing, preloading, correct fill usage) are solid, the next level is how next/image behaves when the app actually scales: high traffic, global users, dynamic content from CMS/user uploads, and teams that care about real money (bandwidth bills, origin requests, edge cache hit rates).

These are the patterns that turn “it works locally” into “it survives Cyber Monday without exploding costs or CWV regressions”.

Custom Loaders and CDNs (When It’s Worth It)

By default, Next.js handles image optimization via its built-in loader (/_next/image route on Vercel/Netlify, or equivalent on self-hosted).
It’s excellent for 90% of apps — but at serious scale it can become expensive or slow.

When to switch to a custom loader + external CDN (Cloudinary, Imgix, Akamai, ImageKit, or Vercel’s Edge Network with custom config):

  • You’re burning through Vercel bandwidth/image optimization limits
  • You need advanced transformations (smart cropping, face-aware resize, AI enhancement, watermarking)
  • You want images served from POPs even closer to users than Vercel’s edge
  • You already pay for a dedicated image CDN anyway

Minimal custom loader setup (Imgix example):

// next.config.js
module.exports = {
  images: {
    loader: 'custom',
    loaderFile: './lib/custom-image-loader.js',
  },
};

Rule of thumb (when to actually do this):

  • If your monthly image bandwidth > ~50–100 GB → seriously evaluate
  • If you need per-image analytics or transformations → yes
  • If you’re a small/medium site and Vercel costs are fine → stay default — custom loaders add complexity, debugging pain, and another vendor

Remote Images and Security Gotchas

Any image coming from outside your domain (CMS, S3, user uploads, Unsplash, etc.) must be explicitly allowed via remotePatterns — otherwise Next.js blocks it at build/runtime to prevent SSRF (Server-Side Request Forgery) attacks .

Secure pattern (tightest practical config):

images: {
  remotePatterns: [
    {
      protocol: 'https',
      hostname: 'images.example-cms.com',
      pathname: '/media/**',
    },
    {
      protocol: 'https',
      hostname: '**.cloudinary.com',
    },
    {
      protocol: 'https',
      hostname: 'images.unsplash.com',
    },
  ],
}

Common dangerous patterns we still see in production audits (2026):

  • hostname: ‘*’ → complete open SSRF hole
  • hostname: ‘localhost’ or 127.0.0.1 left from dev → production RCE risk
  • Wildcard on hostname + no pathname → attacker can proxy internal services
  • Forgetting to add new domains after CMS migration → broken images + 500s

Rule of thumb (security checklist):

  • Never use * on hostname unless you have a very strong reason + tight pathname
  • Always specify protocol: ‘https’
  • Use pathname globs (/**) only for known prefixes
  • Review remotePatterns in every major CMS/asset-provider change

Bandwidth and Cost Considerations at Scale

Optimization saves money — misconfiguration wastes it very quickly.

Real-world numbers from production Next.js apps (2025–2026 data):

  • One unoptimized 4K hero image repeated across 100k pageviews → 5–12 MB per view → $800–$3,000/month extra CDN cost
  • Over-preloading 8–12 images per page → 2–4× bandwidth for bounce sessions
  • No aggressive minimumCacheTTL → repeated origin fetches → higher compute + slower TTFB

Quick, high-impact config wins:

images: {
  minimumCacheTTL: 2592000, // 30 days for static/near-static images
  formats: ['image/avif', 'image/webp'], // force modern formats
  quality: 75, // default balance (lower = more savings, test visually)
}

Monitor:

  • Vercel → Usage → Image Optimization tab
  • Cloudflare/Cloudinary/Akamai dashboards for origin hit rate
  • Real-user field data (CrUX, SpeedCurve, Web Vitals extension)

If your image-related costs are climbing or CWV is still red under load, image optimization is only one piece — the full system-level fixes (caching, rendering strategies, bundle splitting) are covered in depth in our performance playbook.

Content in the sectionSource anchor / section used
Static imports auto-extract dimensions#width-and-height
Remote images require manual width/height#remote-images + #width-and-height
sizes prop purpose & syntax#sizes
Mobile-first + capping max width example#sizes (examples & guidance)
fill correct usage + parent requirements#fill
Unconstrained fill causes CLS#fill (warnings)
preload replacing priority#preload
General CLS prevention responsibility#width-and-height (implicit via aspect ratio docs)

Migrating from Legacy or Broken next/image Usage

Many production Next.js apps still run on patterns that worked fine in Next.js 12–13 but are now actively harmful in 2026 (v16+): deprecated props, unconstrained fill, over-preloading, missing sizes, or legacy wrappers that bloat bundles.

Migration is usually low-risk if done systematically — but skip steps and you’ll introduce regressions (CLS spikes, LCP jumps, INP delays).

Here’s a practical, battle-tested checklist we use in audits and upgrades. Run it page-by-page or component-by-component.

Migration Checklist

1. Replace deprecated priority → preload

  • Find all
  • Replace with preload={true} only on confirmed LCP heroes
  • Remove from carousels, lists, below-fold images — they hurt FCP “`tsx

// Before (legacy)

// After

2. Fix unconstrained fill layouts (top CLS source)

  • Search for fill without position: relative parent or aspect ratioWrap in a container with defined ratio
// Bad (causes shift) 
<div className="w-full">
  <Image src={hero} fill alt="Hero" />
</div> 

// Fixed 
<div className="relative aspect-[16/9] w-full"> 
  <Image src={hero} fill sizes="(max-width: 768px) 100vw, 50vw" alt="Hero" className="object-cover" /> 
</div>

3. Add missing sizes to responsive images

  • Any fill or large hero without sizes → defaults to 100vw → oversized desktop downloadsAdd realistic media-query based sizes
// Before: no sizes → desktop gets huge image
<Image
  src={product}
  fill
  alt="Product"
/>

// After
<Image
  src={product}
  fill
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px"
  alt="Product"
/>

4. Remove or unoptimized tiny/static assets

  • SVGs, icons, logos <10 KB, animated GIFs/WebPAdd unoptimized or switch to <img>
// Legacy: next/image on icon → unnecessary wrapper
<Image
  src="/logo.svg"
  width={32}
  height={32}
  alt="Logo"
/>

// Better
<Image
  src="/logo.svg"
  width={32}
  height={32}
  alt="Logo"
  unoptimized
/>

// or just
<img
  src="/logo.svg"
  width="32"
  height="32"
  alt="Logo"
/>

5. Audit over-preloading and decode pressure

  • Search for multiple preload={true} on the same pageLimit to one per page (true LCP hero)For large below-fold images → consider decoding=”async” if needed (though Next.js defaults help)

Quick audit tip: Open Chrome DevTools → Network → filter images → check if multiple preloads fire early. → Keep only the hero; remove the rest.

6. Test post-migration CWV (critical)

  • Run Lighthouse on key pages before/after
  • Use Web Vitals Chrome extension or Vercel Analytics for field data
  • Watch for regressions: CLS >0.1, LCP >2.5s, INP >200ms
  • Fix any new shifts by double-checking dimensions/aspect ratios

Pro tip: Use a codemod or simple grep + replace for steps 1–3 across the codebase. Then manually review fill usages (they cause 60–70% of post-migration CLS spikes in our experience).

If you finish this checklist and still see red metrics or high image costs, the issue is rarely just images — it’s usually systemic (rendering mode, caching, bundle size). That’s where our broader playbook helps

Common Mistakes (Quick Reference)

Here are the most frequent next/image mistakes we still see in production Next.js apps in 2026 — ranked roughly by how often they cause measurable CWV damage (CLS, LCP, INP) or unnecessary costs.

Quick list — scan it, search your codebase for these patterns, fix the highest-impact ones first.

  • Preloading too many images (or the wrong ones) Classic: preload={true} on carousels, product grids, or every hero on the page. Result: FCP/LCP delay + wasted bandwidth. Fix: Limit to exactly one true LCP hero per page. Remove from everything else.
  • Using fill without a constrained parent container Classic: <Image fill /> inside a div without position: relative + aspect ratio. Result: Massive CLS when image loads (often 0.2–0.5+). Fix: Always wrap in <div className=”relative aspect-[ratio]”> or equivalent.
  • No or incorrect sizes prop on responsive images Classic: sizes=”100vw” everywhere (or missing completely). Result: Desktop browsers download 4K images for 800px containers → slow LCP + high data usage. Fix: Use realistic media queries: (max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px.
  • Forgetting explicit width and height on remote images Classic: <Image src=”https://…” fill /> with no dimensions. Result: Fallback to 0×0 → huge layout shift on load. Fix: Always provide width/height or use static import.
  • Applying next/image to icons, SVGs, logos or tiny assets (<10 KB) Classic: Wrapper + placeholder logic on 32×32 SVG icon. Result: Unnecessary hydration cost + rasterization of vectors. Fix: Use unoptimized or switch to plain <img> (see our earlier section on when NOT to use next/image).
  • Overriding quality too low without visual testing Classic: quality={40} globally because “smaller is better”. Result: Blurry images on high-DPI screens → poor user experience. Fix: Default 75, lower selectively and test visually on real devices.
  • Not setting minimumCacheTTL for static/near-static images Classic: Leaving default 60s TTL. Result: Repeated origin fetches → higher costs + slower TTFB for repeat visitors. Fix: Set minimumCacheTTL: 2592000 (30 days) in next.config.js for cached assets.

For a broader list of Next.js-specific performance mistakes (including but not limited to images), check our existing article: Common Next.js Mistakes That Kill Core Web Vitals

And for a live walkthrough of many of these issues (including before/after metrics), watch the video: Fixing Real Performance Issues in Next.js

Fix the top 3–4 from this list and you’ll usually see the biggest CWV jump. The rest are polish.

Final Thoughts: When next/image Is Worth It — and When It’s Not

After all the patterns, checklists, benchmarks, and gotchas covered in this guide, one truth stands above everything else in 2026:

next/image is an excellent tool — but it is not a performance silver bullet, and it is not the right choice for every image on your site.

It shines brightest when used deliberately on the right assets, with correct layout constraints, sizing, and loading strategy. When misused or over-applied, it can introduce CLS, INP regressions, hydration overhead, bundle bloat, and even higher bandwidth costs than plain [image] would have caused.

When next/image Is Worth It (Use It Confidently)

  • Any content image that is:
    • Above the fold or likely LCP candidate (hero banners, product main images, featured visuals)
    • Served remotely (CMS, S3, external providers) — automatic format negotiation + resizing saves massive effort
    • Large enough to benefit from AVIF/WebP conversion and responsive variants (generally > 20–30 KB)
    • Part of lists or grids where lazy loading + blur-up placeholder improves perceived performance
  • When your team wants to:
    • Eliminate manual srcset/sizes boilerplate
    • Guarantee modern format delivery without polyfills
    • Prevent basic CLS from missing dimensions (when you actually supply them correctly)

In short: next/image is worth it for most content/media images in modern Next.js apps — especially if you follow the sizing, fill, and preloading rules from earlier sections.

When next/image Is Not Worth It (Skip or Unoptimize)

  • Icons, logos, favicons, small UI graphics (<10–15 KB)
  • Pure vector SVGs (next/image rasterizes them → loses sharpness + adds overhead)
  • Animated GIFs or APNGs (animation often breaks unless unoptimized)
  • Tiny decorative spacers, 1×1 trackers, or placeholder blanks
  • Cases where you need pixel-perfect control or no wrapper divs (e.g., inline in markdown content)

In these situations, plain [image] (with loading=”lazy” where appropriate) or even CSS background-image is usually lighter, faster, and simpler.

Quick decision rule (copy-paste for team docs): If the image is:

  • Content-focused, >20 KB, benefits from responsive formats → use next/image
  • UI/decorative, <10 KB, vector, animated, or needs zero overhead → use [image] or unoptimized

Bottom Line

Performance is never about one component — it’s systemic.

next/image can give you 30–70% payload reduction and better defaults when used correctly, but blind adoption (“replace every img tag”) is one of the most common ways teams accidentally hurt Core Web Vitals.

Measure before and after every major change. Profile real-user data (not just Lighthouse). And remember: the best optimization is often the one you don’t need to do at all.

If you’re still chasing green scores or lower image bills after applying everything here, the root cause is rarely isolated to images — it’s usually rendering modes, caching layers, client-side JavaScript volume, or routing patterns. Or contact us for a free quick audit or consultation 🙂

Thanks for reading — now go measure something.

Jakub Dakowicz

Jakub is the Chief Technology Officer at Pagepro, where he leads technical strategy and oversees the architecture of complex web platforms built with Next.js and headless CMS solutions. With nearly nine years at Pagepro and over five years leading the engineering team, he has been instrumental in shaping the company’s architectural standards, development workflows, and scalability practices. Jakub focuses on building robust, composable systems that balance performance, maintainability, and long-term business flexibility. He drives technical decision-making across projects, ensuring that solutions are not only modern, but strategically aligned with client growth.

Article link copied

Close button

Leave a Reply

* Required informations.