If your responsive images look squished or stretched, you almost certainly set both a width and a height in CSS. An image has an intrinsic aspect ratio, and the moment you pin both dimensions to fixed values that do not match that ratio, the browser distorts the picture to obey you. The fix is one rule:
img {
max-width: 100%;
height: auto;
}max-width: 100% stops the image from overflowing its container (it shrinks to fit on small screens but never blows up past its natural size), and height: auto tells the browser to compute the height from the width so the aspect ratio is preserved. That is the whole job for the common case. The rest of this page is why it goes wrong, why you still want the HTML width and height attributes, and what to do when you genuinely need a fixed box.
Why the squish happens
The distortion comes from over-specifying. Any of these will do it:
/* Both fixed, ratio ignored: stretched */
img { width: 400px; height: 300px; }
/* Fluid width, fixed height: squished as the column narrows */
img { width: 100%; height: 200px; }In the first rule you forced a 4:3 box onto, say, a 16:9 photo, so the browser squashes the photo to fit. In the second, the width tracks the container while the height stays nailed at 200px, so the image gets more and more distorted as the viewport shrinks. A replaced element like <img> only keeps its proportions when at most one of its two dimensions is fixed and the other is left to follow.
So the rule is simple: set one dimension, let the other be auto. With responsive layouts the width is the one that flexes, so width (or max-width) is what you control and height: auto is what you hand back to the browser.
Keep the width and height HTML attributes anyway
Here is the part people get wrong: because they are setting height: auto in CSS, they assume they should strip the width and height attributes off the <img> tag. Do not. Those two things solve different problems and cooperate cleanly.
<img src="photo.jpg" width="1600" height="900" alt="...">The width and height attributes are the image's intrinsic size. The browser divides them to get the aspect ratio and reserves a correctly-shaped box before the file downloads. Without them, a width: 100%; height: auto image collapses to zero height until it loads, then shoves the page down when it arrives, which is exactly the layout shift that Cumulative Layout Shift (CLS) measures and Core Web Vitals penalises.
The CSS height: auto does not fight the attributes. The browser uses the attribute ratio to size the placeholder, then the responsive CSS scales the displayed image while holding that same ratio. Attributes for layout reservation, CSS for fluid display. You want both.
aspect-ratio: the modern reinforcement
If the markup cannot carry width/height attributes (a background-ish hero, a CMS that strips them, an element that is not an <img> at all), the aspect-ratio property reserves the box from CSS instead:
img {
max-width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}aspect-ratio: 16 / 9 tells the browser the shape ahead of time, so it can reserve space the same way the HTML attributes do. It is Baseline across current browsers, so you can lean on it in production. When the <img> already has matching width/height attributes, you do not need this too. It is the fallback for the cases where the attributes are not available.
When you DO need a fixed box: object-fit
Sometimes a fixed box is the design: a grid of thumbnails that must all be the same square, an avatar, a card header that has to be exactly 320 by 180 regardless of the source photo. There you do set both dimensions, and you stop the distortion with object-fit instead of height: auto:
.thumb {
width: 320px;
height: 180px;
object-fit: cover;
}object-fit controls how the image fills the box you forced on it, without stretching:
coverscales the image to fill the whole box and crops whatever overflows. Best for thumbnails and hero crops where edge loss is acceptable.containscales it to fit entirely inside the box, leaving letterbox or pillarbox bars. Best when the whole image must stay visible (logos, product shots).fillis the default, and it is the one that stretches. That is the behaviour you are escaping.
Pair it with object-position to choose which part survives the crop (object-position: center top keeps faces in frame). object-fit is Baseline and safe to use everywhere. The mental model: height: auto keeps the box matching the image; object-fit keeps the image matching the box.
Inside grid and flex containers
One more squish source worth naming: an <img> placed directly as a flex item can stretch along the cross axis because flex containers stretch items by default. If a sidebar image looks oddly tall, that is usually it. Keep the max-width: 100%; height: auto rule and let the image sit in a block wrapper, or set align-self: start on the item so the flex container stops stretching it.
For the wider picture of srcset, sizes, and serving the right file to each screen, see responsive images done properly. When the box dimensions come from a calculation rather than a literal value, CSS calc() is how you express them. And if you are styling images conditionally based on whether a wrapper has a caption or a particular class, the :has() parent selector makes that selection possible without JavaScript.
FAQ
See also
- Responsive images done properly: srcset, sizes, and serving the right file to each viewport.
- CSS calc() for real layouts: compute box dimensions instead of hard-coding them.
- The :has() parent selector: style images based on their wrapper or sibling context, no JavaScript.
Sources
Authoritative references this article was fact-checked against.
- object-fit, CSS reference (MDN)developer.mozilla.org
- aspect-ratio, CSS reference (MDN)developer.mozilla.org
- Aspect ratio mapping for images (MDN)developer.mozilla.org
- Optimize Cumulative Layout Shift (web.dev)web.dev
- Understanding and setting aspect ratios (MDN)developer.mozilla.org





