ScreenshotNeo

BlogGuides

How to Improve Image Performance: Advanced Optimization Techniques

Speed up image-heavy pages by removing unnecessary requests, serving responsive and well-compressed assets, and fixing the actual LCP bottleneck.

By the ScreenshotNeo team4 October 202610 min read

To improve image performance, first remove image requests the page does not need. Then serve assets close to their rendered dimensions, choose a format and compression level that preserve acceptable visual quality, and load images according to their importance and position. Measure the Largest Contentful Paint (LCP) phases before choosing a fix: a smaller file helps transfer time, but not late discovery or delayed rendering.

This workflow applies to product pages, articles, galleries, and image-heavy apps. Work through it in order so you can tell which change actually helped.

1. Remove unnecessary image requests

The fastest image to download is one the page does not request. Audit pages for duplicate images, decorative assets that can be CSS, hidden content loaded eagerly, obsolete variants, and images below a point users are unlikely to reach. Do not remove meaningful content merely to improve a score; remove requests that do not serve the page.

  1. Open browser developer tools and inspect the Network panel with the Img filter enabled.
  2. Reload the page with the cache disabled and record image URLs, transferred bytes, dimensions, and initiators.
  3. Check for duplicate URLs, unexpectedly large files, hidden carousel slides, and images requested before they are needed.
  4. Remove or defer requests that are not needed for the initial view. Recheck interactions so deferred gallery or carousel images still load when requested.

Be careful with CSS backgrounds: they can be appropriate for decoration, but meaningful content generally belongs in an image element with useful alternative text. Images referenced by CSS can also be discovered later than images in the initial HTML.

2. Deliver images at appropriate dimensions

Find the rendered slot size at common viewport widths, then provide a small, measured set of candidates. The browser can select among srcset candidates using the layout information in sizes and the device’s pixel density.

<img
  src="/images/product-800.webp"
  srcset="/images/product-400.webp 400w,
          /images/product-800.webp 800w,
          /images/product-1200.webp 1200w"
  sizes="(max-width: 600px) 100vw, (max-width: 1100px) 50vw, 560px"
  width="1200"
  height="800"
  alt="Blue ceramic mug on a wooden table">

The sizes value should describe the actual rendered slot, not simply repeat the image’s intrinsic dimensions. If the slot is full width on small screens and about half the viewport on wider screens, the example is a reasonable starting shape; adjust its breakpoints and maximum width to match your layout.

Responsive-image guidance on web.dev illustrates that a desktop-sized image sent to a phone can use 2–4 times more data than needed in that scenario. That is an example, not a guaranteed saving for every site. More variants can improve fit but add markup, cache entries, storage, and image processing work. Start with a few useful widths and add candidates only when measurements show a meaningful gap.

Use <picture> for art direction or format fallback

Use <picture> when you need to change the crop or composition at a breakpoint, or to offer format alternatives with a fallback. The browser selects the first supported matching source; the img remains the fallback and carries dimensions and alternative text.

<picture>
  <source
    type="image/avif"
    srcset="/images/landscape-480.avif 480w, /images/landscape-960.avif 960w"
    sizes="(max-width: 700px) 100vw, 960px">
  <source
    type="image/webp"
    srcset="/images/landscape-480.webp 480w, /images/landscape-960.webp 960w"
    sizes="(max-width: 700px) 100vw, 960px">
  <img
    src="/images/landscape-960.jpg"
    width="1600"
    height="900"
    alt="A mountain lake at sunrise"
    loading="lazy">
</picture>

Keep candidate sets aligned across sources and test actual browser behavior so fallback markup does not cause unintended extra downloads. If a server negotiates a format from the request’s Accept header, configure shared caches to distinguish representations, commonly with Vary: Accept, and confirm the cache key respects that header.

3. Choose format and compression by image content

Content Good starting choice What to inspect
Photographs and rich textures Compare WebP and AVIF with your current raster format Texture, gradients, color, and artifacts at the displayed size
Logos, diagrams, charts, line art SVG when the artwork is genuinely vector Rendering at different sizes and any embedded raster content
Transparency or crisp flat graphics Compare lossless WebP, PNG, or SVG as appropriate Edges, alpha/transparency, file size, and toolchain support
Existing JPEG or PNG assets Keep if conversion does not improve the real result Compatibility, decoded appearance, and total bytes

WebP supports lossy and lossless compression and transparency. AVIF also supports lossy and lossless modes and modern color features. Browser support and encoder behavior depend on the browsers and publishing tools you must support, so test that stack rather than assuming a format is universally suitable. SVG is effective for true vector graphics; it is not a replacement for photographic imagery.

Lossy compression discards image information. It can reduce bytes substantially, but may make colored text, sharp edges, and fine detail look poor. Lossless compression preserves image data, though byte savings vary. There is no universal quality setting: encode representative examples, compare them at their intended rendered size, and select the smallest version that meets your visual-quality bar. Tools such as Squoosh and ImageOptim can help with manual comparisons.

4. Set loading behavior and reserve image space

Give images explicit width and height attributes whenever their intrinsic dimensions are known. CSS can still make the image responsive:

img {
  max-width: 100%;
  height: auto;
}

The dimensions give the browser an aspect ratio to reserve before the image arrives, reducing layout movement. Use native lazy loading for images below the fold:

<img src="/images/related-story.webp"
     width="640" height="427"
     loading="lazy"
     alt="A gardener tending seedlings">

Do not lazy-load a visible hero or likely LCP image. A lazy image can wait for layout before the browser decides whether it is near the viewport, delaying request discovery. Browser-native loading="lazy" covers the basic case; a JavaScript lazy-loading library is generally unnecessary for that behavior.

Use priority hints only for a diagnosed bottleneck

If the likely LCP image is present in the initial HTML but still starts too late, consider fetchpriority="high" on that one image. Raising its priority can reduce priority available to other resources, so do not apply it broadly.

<img src="/images/home-hero.webp"
     srcset="/images/home-hero-800.webp 800w, /images/home-hero-1600.webp 1600w"
     sizes="100vw"
     width="1600" height="900"
     fetchpriority="high"
     alt="A runner on a forest trail at dawn">

If the critical image is only discoverable through CSS or JavaScript, first consider making it available earlier in the HTML. A preload can help when evidence shows discovery is late and the image cannot reasonably be exposed earlier. Avoid broad preloading: it can compete with stylesheets, fonts, and other critical resources. Match a preload’s URL and responsive attributes to the actual image request to avoid fetching two variants.

5. Identify the LCP image and its bottleneck

LCP measures when the largest image or text block in the viewport is rendered. If an image is the LCP element, separate its timing into three practical phases:

Phase What it means Likely action
Resource load delay Time before the browser discovers and starts requesting the image Put it in early HTML, remove lazy loading, or selectively preload when discovery is genuinely late
Resource load duration Time spent transferring the image Reduce bytes with suitable dimensions, format, compression, and delivery
Element render delay Time after the resource is available before it is rendered Inspect CSS, layout, main-thread work, visibility, and rendering dependencies

Use browser performance tools or field data to identify the LCP element and inspect its request timing. Compare the same route and conditions before and after each change. A smaller file may barely improve LCP when the real problem is late discovery or delayed rendering.

web.dev’s LCP guidance, updated March 31, 2025, describes a good experience as LCP at or below 2.5 seconds for at least 75% of visits, and values above 4 seconds as poor. Check the current guidance when using thresholds in a performance target because metric recommendations can change.

6. Consider an image CDN when the workflow justifies it

An image CDN can create and serve variants for size, density, format, and compression. It can be useful when a site has many images, many responsive variants, frequent content uploads, or a need to transform at request time. It is not an automatic requirement.

For a small site or stable image library, build-time optimization may be simpler. Before adopting a service, compare its cost, support, documentation, migration effort, origin architecture, cache behavior, and whether a new cross-origin connection adds setup time. A CDN that proxies through the site’s main origin can avoid an additional origin connection. web.dev reports broad possible file-size savings from CDN adoption, but those figures are not a promise for an individual site; measure your own image set and total page performance.

7. A practical implementation checklist

  1. Capture a baseline: image request count, transferred bytes, layout movement, and LCP element and phases.
  2. Remove duplicate, obsolete, or unnecessary image requests.
  3. Generate a measured set of image widths for common rendered slots and write truthful sizes values.
  4. Compare suitable formats and compression on representative content at actual display sizes.
  5. Add intrinsic width and height to image elements.
  6. Lazy-load below-the-fold images; keep the hero and likely LCP image eager.
  7. Use high fetch priority or preload only when timing evidence supports it.
  8. Re-measure under comparable conditions and check visual quality, not just bytes.
  9. Review cache behavior, fallback formats, and any extra CDN connection if delivery architecture changed.

8. Troubleshooting common image performance problems

Symptom Likely cause Fix
The page downloads a huge image on a phone Single desktop asset, missing or inaccurate srcset/sizes, or wrong candidate widths Inspect the chosen candidate and rendered slot; add useful widths and correct the sizes expression
The hero image appears late despite a small file It is lazy-loaded, discovered through CSS/JavaScript, or given low priority Remove lazy loading from the hero, expose it in early HTML, then consider selective priority or preload
LCP does not improve after compression Load delay or render delay dominates transfer duration Inspect LCP phases and address discovery or rendering instead of reducing bytes further
Images look soft or show halos and blocks Excessive lossy compression, poor source dimensions, or unsuitable format Compare at rendered size, raise quality or switch encoding, and inspect text and high-contrast edges
Layout jumps when images load No intrinsic dimensions or reserved aspect ratio Set width/height or an equivalent CSS aspect ratio before the request completes
Lazy images never appear in an interactive gallery Custom visibility logic, hidden container behavior, or deferred source setup is incorrect Check the element’s src/srcset, visibility, and console/network errors; load on interaction if needed
Modern format conversion increases requests or breaks caches Fallback markup or cache keys do not match representation negotiation Inspect actual requests; align picture candidates or configure negotiated responses and Vary: Accept
Preloading makes the page slower Too many resources compete for early bandwidth or the preload does not match the eventual request Remove unnecessary preloads and match URL, candidate selection, and responsive attributes to the consuming image

9. Performance, reliability, and cost notes

  • Performance: fewer requests and fewer bytes reduce network contention, but a faster transfer does not guarantee a faster render. Measure each LCP phase and the whole page.
  • Reliability: keep a tested fallback when serving modern formats, and verify cache keys when responses vary by headers. A transformation pipeline should be checked for missing or stale variants.
  • Storage and operations: more source widths mean more generated assets, cache entries, and processing. Select variants based on real layouts.
  • Cost: compare the work and infrastructure for build-time conversion with hosted transformation costs. There is no universal break-even point; image count, traffic, transformations, and operational effort determine it.
  • Quality: judge encodings at the displayed size on representative photos, transparency, line art, and text-heavy graphics. Byte count alone is not an acceptance criterion.

Or skip the browser setup

For clean screenshots of a page while validating its visual output, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF; its options include full-page capture with lazy images loaded, viewport and device presets, custom CSS and JavaScript, and waits for selectors or network idle. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Should I convert every image to AVIF?

No. Compare formats on representative assets, verify browser and toolchain support, and keep a suitable fallback where needed.

Does lazy loading always improve page speed?

No. It helps defer offscreen requests, but applying it to initial-viewport or LCP images can delay discovery.

How many responsive image widths should I generate?

Start with a limited set that covers common rendered sizes and density needs. Add candidates when real measurements show the current set is wasteful.

When is an image CDN worth considering?

Consider one when image volume, transformations, or operational needs justify its cost and setup. A stable, small library may be easier to optimize during the build.

Primary references