<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
sizes="(min-width: 760px) 720px, calc(100vw - 32px)"
width="1600"
height="900"
alt="A terrier sitting beside a garden path"
/>img {
max-width: 100%;
height: auto;
}The HTML lets the browser choose an appropriate resource. The CSS lets the image fit its container without changing its proportions. The width and height attributes reserve the intended aspect ratio while the file loads. I check all three together when an image is blurry, too large or visibly squashed.
The sizes value above assumes a content column capped at 720 CSS pixels with 16-pixel side gutters on smaller screens. Change it to match your layout; it does not set that layout for you.
Resolution switching: srcset and sizes
Use width descriptors for different resolutions of the same image:
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w, photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
width="1600" height="900"
alt="A terrier beside a garden path"
/>Each w value must describe the actual pixel width of that resource. sizes describes its expected displayed width in CSS pixels. A 375 CSS-pixel slot on a 2x display suggests a resource around 750 pixels wide, but the browser makes the final choice using its own selection policy, cache and conditions. Do not rely on it always choosing one exact candidate.
For a fixed-size avatar, density descriptors are simpler:
<img src="avatar-48.png"
srcset="avatar-48.png 1x, avatar-96.png 2x, avatar-144.png 3x"
width="48" height="48" alt="Author portrait" />Do not mix width and density descriptors in the same srcset.
The sizes gotcha
For a normal width-descriptor image without an applicable sizes value, the browser uses the default full-viewport slot. That can cause a narrow content-column image to download a larger resource than necessary.
Match the column cap, breakpoints and gutters:
<img src="photo-720.jpg"
srcset="photo-480.jpg 480w, photo-720.jpg 720w, photo-1440.jpg 1440w"
sizes="(min-width: 760px) 720px, calc(100vw - 32px)"
width="1440" height="810" alt="A terrier beside a garden path" />The first matching media condition wins. The final unqualified length is the fallback. sizes is a resource-selection hint, so still use CSS to set the actual layout.
Check the rendered width and currentSrc in browser developer tools. Test on a fresh page load at narrow and wide widths, including a high-density display. A browser may retain an already-loaded larger candidate when you resize; that is not proof your smaller candidate is broken.
Why images look squished
A rule such as width: 100%; height: 200px forces a box whose ratio changes with the container. The default object-fit: fill stretches the image into that box.
For a natural, uncropped image:
.article-image {
max-width: 100%;
height: auto;
display: block;
}max-width caps the width at the container; with an otherwise automatic width, a smaller image normally keeps its intrinsic size. An explicit larger width elsewhere can still enlarge it, so inspect the winning CSS rules. To intentionally fill the available width, use width: 100%; height: auto and provide enough source pixels for that display size.
Keep the width and height HTML attributes
<img src="photo.jpg" width="1600" height="900" alt="A terrier beside a garden path" />These dimensions provide an initial ratio before the image downloads. CSS can resize the displayed image while keeping that ratio. Removing the attributes without reserving space another way can produce layout shift when the image arrives.
Use dimensions that agree with the intended source ratio. Correct width/height attributes are a straightforward default, but equivalent space reservation through CSS is also possible; the attributes are not a syntactic requirement for every valid image.
Reserve a ratio with CSS
When the markup cannot carry the dimensions, CSS can provide a fallback ratio:
.article-image {
width: 100%;
height: auto;
aspect-ratio: auto 16 / 9;
}For an image, auto 16 / 9 uses the supplied ratio before the intrinsic ratio is available, then uses the image's natural ratio. If those ratios differ, the box can still change after loading. Use the correct known ratio when you need stable space.
A plain aspect-ratio: 16 / 9 deliberately keeps that preferred ratio; it is not a universal repair for photos with different proportions. For a fixed crop, combine a chosen box with object-fit.
Fit an image into a fixed box
.thumbnail {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
display: block;
}cover fills the box and crops excess content. contain keeps the complete image visible and can leave empty space. fill stretches it. object-position chooses which part of a cropped image stays visible:
.portrait {
width: 320px;
height: 180px;
object-fit: cover;
object-position: center top;
}For an image directly inside a flex container, cross-axis stretching can be another cause of distortion. Use a block wrapper or align-self: start when stretching is unwanted. Check the parent layout as well as the image's own width and height.
Art direction: picture with media
When mobile needs a different crop, select it with <picture>:
<picture>
<source media="(min-width: 800px)"
srcset="hero-wide.jpg" width="1600" height="900" />
<source media="(min-width: 500px)"
srcset="hero-medium.jpg" width="900" height="600" />
<img src="hero-square.jpg" width="600" height="600"
alt="A terrier standing beside a garden path" />
</picture>The browser considers sources in order and chooses an applicable supported source. The <img> provides the rendered element, fallback and text alternative. Matching dimensions on art-directed sources let supporting browsers reserve the selected crop's ratio instead of assuming the square fallback ratio everywhere.
A <source> can also have its own width-descriptor srcset and sizes when each crop needs several resolutions. Keep the alternative text accurate for every selected crop.
Format fallback: picture with type
<picture>
<source type="image/avif" srcset="photo.avif" />
<source type="image/webp" srcset="photo.webp" />
<img src="photo.jpg" width="1200" height="800"
alt="A terrier beside a garden path" />
</picture>The first supported source type wins. Compare actual image quality and transfer sizes when deciding the order; one format is not always smaller for every picture and encoder setting. Keep the fallback and every alternative available and correctly encoded.
Format and resolution selection can be combined:
<picture>
<source type="image/avif"
srcset="photo-400.avif 400w, photo-800.avif 800w, photo-1600.avif 1600w"
sizes="(min-width: 760px) 720px, calc(100vw - 32px)" />
<source type="image/webp"
srcset="photo-400.webp 400w, photo-800.webp 800w, photo-1600.webp 1600w"
sizes="(min-width: 760px) 720px, calc(100vw - 32px)" />
<img src="photo-800.jpg" width="1600" height="900"
alt="A terrier beside a garden path" />
</picture>Lazy loading, decoding and fetch priority
For an image well below the initial viewport:
<img src="photo.jpg" width="1200" height="800"
loading="lazy" decoding="async"
alt="A terrier beside a garden path" />For the likely Largest Contentful Paint image near the top of the page:
<img src="hero.jpg" width="1600" height="900"
fetchpriority="high"
alt="A terrier in the garden" />Avoid lazy-loading the LCP image. fetchpriority is a hint about relative priority, not a command to bypass the browser's scheduler. Apply high priority selectively and measure the result; flagging every image high removes the distinction you wanted.
decoding="async" hints that the next paint need not wait for the image to decode. It does not guarantee which thread performs decoding. For most static images, dimensions, resource size, discoverability and loading priority matter more than this one hint.
Client Hints and the native HTML default
Older tutorials may use the legacy DPR, Width and Viewport-Width request headers. Those legacy device hints are deprecated. Newer Sec-CH- hints have their own support and server-negotiation requirements; deprecation of the older names does not mean every Client Hints feature is dead.
For an ordinary responsive image, srcset, sizes and picture provide a portable browser-controlled path without requiring a cooperating image server. A CDN-specific hint workflow still needs a fallback and careful cache configuration. Check the current documentation for the exact header instead of assuming universal browser support.
Quick decision guide
| Need | Use |
|---|---|
| Same image, fluid display width | srcset with width descriptors and accurate sizes |
| Fixed CSS size, different pixel densities | Density descriptors such as 1x and 2x |
| Different crop at a breakpoint | picture and source media |
| Alternate file formats | source type with an image fallback |
| Natural, uncropped proportions | Responsive width and height: auto |
| Uniform cropped thumbnails | A fixed ratio or dimensions with object-fit: cover |
| Less loading shift | Correct dimension attributes or equivalent reserved space |
FAQ
See also
Sources
Authoritative references this article was fact-checked against.
- HTML responsive-image selectionhtml.spec.whatwg.org
- MDN image attributes and loading hintsdeveloper.mozilla.org
- MDN aspect-ratiodeveloper.mozilla.org
- MDN object-fitdeveloper.mozilla.org
- Legacy Viewport-Width deprecationdeveloper.mozilla.org
- Sec-CH-DPR compatibility and negotiationdeveloper.mozilla.org





