Alcott

Loading site
Skip to content

2026-09-22 · 7 min · By Alcott Dube

How to serve images faster with responsive image optimisation

I treat image delivery as three decisions: which file to send, how much detail the screen needs, and when the browser should request it.

Three textured planes of different sizes, with the smallest fitted precisely into a recessed frame.

I make image-heavy pages feel faster by serving compressed images in suitable formats, giving the browser accurate responsive source choices, and reserving space before files arrive. I load the main visible image promptly, defer images further down the page, and check actual downloads rather than trusting export settings.

Choose avif, webp or jpeg by testing the actual image

I start with the image's job, not its extension. A photographic hero, a transparent product cutout and a diagram need different treatment. For photographs, I compare avif and webp against the existing jpeg at the largest intended display size. For a simple vector illustration, I usually keep the vector rather than turning sharp lines into pixels.

Smaller isn't automatically better. I inspect gradients, fine texture, skin tones and hard edges at the size people will actually see. Quality numbers aren't comparable between encoders, so a setting of 70 in one format doesn't establish a fair match with another. I reduce quality until damage becomes visible, then move back enough to leave some margin.

I use the picture element when I need format alternatives: avif first, webp next, and an img fallback. The browser selects a supported source type; it doesn't compare their visual quality for me. I skip extra variants when the saving is negligible. Maintaining three nearly identical files for a tiny thumbnail can cost more engineering time than the download reduction justifies.

Set srcset and sizes to match the rendered image width

Responsive image optimisation depends on telling the browser two separate things. The srcset attribute lists available files and their actual pixel widths. The sizes attribute describes the image's expected width in the layout. As the MDN responsive images guide explains, the browser combines those hints with factors including screen density to choose a candidate. I don't try to reproduce that selection logic in JavaScript.

For an image that fills a narrow screen with 16 pixels of padding on each side, then stops growing at 720 pixels, I might use `sizes="(max-width: 752px) calc(100vw - 32px), 720px"`. That description must match the stylesheet. If the image actually sits in a half-width column on desktop, declaring a full viewport width gives the browser an inflated estimate and can trigger an unnecessarily large download.

I would pair that layout with candidates such as `srcset="photo-480.webp 480w, photo-768.webp 768w, photo-1080.webp 1080w, photo-1440.webp 1440w"`. Those labels must describe the files' real widths. A 720-pixel slot on a double-density display may need the 1440-pixel candidate. I start with a few useful widths, not dozens. When a mobile layout needs a tighter crop rather than a smaller file, I use picture sources with media conditions for art direction.

An empty rectangular outline and a matching blue panel surrounded by evenly aligned paper blocks.
The empty frame represents space reserved before an image loads.

Add width and height to prevent image layout shifts

I put width and height attributes on content images, even when the stylesheet makes them fluid. An image with `width="1440" height="960"` tells the browser its proportions before the download finishes. With `max-width: 100%; height: auto`, it can still shrink to fit its container. Those attributes establish a ratio; they don't require the image to occupy 1440 pixels on screen.

Without reserved space, a paragraph can appear, move down when the image arrives, and take a link with it just as someone taps. The MDN img reference documents how dimensions help the browser establish an aspect ratio ahead of loading. I treat that as a usability requirement, not a cosmetic adjustment made after the performance work.

For fixed-shape cards, I define the container's aspect ratio and use object-fit to control the crop. I check faces, labels and product edges before accepting `object-fit: cover`. If picture sources switch between different proportions, I make the reserved ratio follow the selected layout. Reserving a landscape rectangle for a portrait mobile crop simply moves the layout problem to a different breakpoint.

Load the hero image early and lazy-load lower images

I don't add lazy loading to every image. The main image visible when the page opens should normally be requested promptly. If it is likely to determine Largest Contentful Paint, I consider `fetchpriority="high"` and keep it discoverable in the initial page markup. Web.dev's loading guidance separates early discovery from request priority: a priority hint cannot recover time spent waiting for a script to create the image.

I reserve `loading="lazy"` for images outside the initial view, such as later product rows or supporting editorial photographs. The browser decides how far ahead to fetch them. I test the boundary rather than assuming the third image is always below the fold. A tall desktop screen and a short phone screen can expose very different amounts of the same page.

I use preload only when discovery is genuinely late, such as an essential background image referenced through a stylesheet. Responsive preloads need matching candidate and sizing information, or the browser may fetch a file it doesn't use. I don't mark an entire gallery as high priority. That creates competition for the image, fonts and other resources that are needed to render the first view.

Check image downloads at real viewport sizes

I test a narrow phone viewport, a wider phone viewport and a desktop layout, including a higher-density screen setting. In the browser's network panel, I check which file was selected, its transferred size and when its request began. I disable the cache for a cold-load check, then test a repeat visit separately. Mixing those conditions makes comparisons difficult to interpret.

My first diagnostic is simple: does the downloaded image roughly match the displayed width multiplied by screen density? It isn't an absolute rule, because browsers retain discretion over source selection. But a 2400-pixel photograph in a 360-pixel slot on a double-density display deserves investigation. I look for missing sizes, inaccurate layout assumptions or a candidate list with a large gap.

I then measure Largest Contentful Paint and Cumulative Layout Shift under a repeatable network profile. Web.dev's guidance on the former breaks the delay into server response, resource discovery, download and rendering. That distinction matters. If the image finishes downloading early but stays hidden behind an animation, another compression pass won't fix the main delay. I also inspect scrolling for blank lazy-loaded areas and compare repeated runs rather than celebrating one fast result.

Build image variants and caching into the publishing workflow

I prefer generating variants during upload or build rather than asking editors to export every size manually. The pipeline should retain the original, record dimensions, avoid enlarging small uploads and produce a limited set of widths. I also want explicit crop controls where composition matters. An automated centre crop is convenient until it removes the part of the photograph that explains the story.

I give generated files versioned addresses so they can use long-lived caching without trapping visitors on an old image. If an image service negotiates formats from request headers, I check that the cache distinguishes those responses correctly. A smaller format helps little if every visit triggers avoidable delivery work or the cache serves an unsuitable variant.

I keep the acceptance check small: correct candidate, acceptable visual quality, stable reserved space and sensible request timing. I repeat it when the layout changes. A new sidebar or wider content column can make yesterday's sizes attribute wrong even though nobody touched the image component.

Questions people ask

Is avif always better than webp?

No. I compare file size and visible damage on the actual image because results vary with content and encoder settings. I keep webp when avif's saving doesn't justify another variant.

Do I need sizes when using srcset?

I provide sizes when srcset uses width descriptors such as 768w. Without an accurate hint, the browser can assume a viewport-wide slot and download more pixels than needed. Density descriptors such as 1x and 2x don't use sizes.

Should I lazy-load the first image on a page?

I don't lazy-load an image that is immediately visible and central to the page. Deferring it can delay the main content. I base the decision on its position in the rendered layout, not its order in the source.

Do width and height attributes stop images being responsive?

No. I use those attributes to establish proportions, then control the displayed size with the stylesheet. For a naturally proportioned fluid image, `max-width: 100%; height: auto` lets it shrink without distortion.

Where I checked my thinking

Start a project