Sizes="auto" pretty much requires width and height attributes
Writing, reading, and maintaining sizes
attributes is the worst part about authoring responsive images; auto-sizes
for lazy-loaded images is a fantastic addition to the platform.
(It is very funny that the Chrome Platform Status entry for sizes=auto
currently describes web developers’ collective opinion about the feature as “no signals.” One-in-ten sizes
attributes on the web are already using auto
, via lazysizes.js! But I digress.)
Auto-sizes just shipped behind the Experimental Web Platform Features flag in Chrome Canary. Which makes this a good time to try to explain the one weird thing about it. In short: in addition to requiring loading=lazy
, sizes=auto
also basically requires that <img>
elements have width
and height
attributes.
But don’t take my word for it. Here’s a note from Section 4.8.2.2 of the HTML specification, Sizes attributes:
Note: […] it is strongly encouraged to specify dimensions using thewidth
andheight
attributes or with CSS. Without specified dimensions, the image will likely render with 300x150 dimensions becausesizes="auto"
impliescontain-intrinsic-size: 300px 150px
What!? 300×150? It’s true! Here’s an <img>
element that uses sizes=auto
:
<img
loading="lazy"
sizes="auto"
srcset="
https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png 1200w,
https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png 900w,
https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png 300w"
src="https://o.img.rodeo/w_300,h_300,b_forestgreen/t_WxH/_.png"
alt="An example image that reports its natural dimensions"
>
And here’s what that looks like right now, in Canary, with the Experimental Web Platform Features flag on:

px
@2x = 600 device pixels).Before I answer some obvious questions (what is happening, why), let’s tackle the practical: how do I fix it?
What values should I use for height
and width
?
You should use the height and width, in pixels, of the largest resource that your <img>
can serve. If you’re using srcset
, that means the dimensions of the largest resource in your srcset
:
<img
loading="lazy"
sizes="auto"
width="1200"
height="1200"
srcset="
https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png 1200w,
https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png 900w,
https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png 300w"
src="https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png"
alt="An example image that reports its natural dimensions"
>
(If you’re using Client Hints, you should use the dimensions of the full-size resource, before any server-side downscaling for delivery. Here’s a (Chrome-only) example that uses Client Hints.)
Depending on what else you have going on in your CSS, adding width
and height
to your HTML might unexpectedly distort your <img>
, giving it a garish, fixed height which will pair terribly with its delightfully flexible width. One line of CSS will fix that for you:
img {
height: auto;
}
<style>
img {
width: 100%;
height: auto;
}
</style>
<img
loading="lazy"
sizes="auto"
width="1200"
height="1200"
srcset="
https://o.img.rodeo/w_1200,h_1200,b_tomato/t_WxH/_.png 1200w,
https://o.img.rodeo/w_900,h_900,b_goldenrod/t_WxH/_.png 900w,
https://o.img.rodeo/w_600,h_600,b_forestgreen/t_WxH/_.png 600w,
https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png 300w"
src="https://o.img.rodeo/w_300,h_300,b_dodgerblue/t_WxH/_.png"
alt="An example image that reports its natural dimensions"
>
And here’s a screenshot of what that looks like in Canary with the flag on:

Perhaps you’re already adding height
and width
to your responsive image HTML and height: auto
to your CSS in order to prevent layout shifts. Great! Change nothing!
Ok, so:
What is happening and why?
By default, images on the web are rendered at their natural (aka intrinsic) dimensions. If you stick a 300×300 resource into an <img>
and do nothing else, its layout size will be 300×300.
The natural dimensions of responsive images get surprisingly weird, fast, but:
- There are a number of ways that a loaded resource’s natural dimensions can affect the layout dimensions of its
<img>
. - The value of
sizes=auto
updates whenever an<img>
’s layout dimensions change, possibly triggering a new resource load. - A new resource will have new natural dimensions, which can affect the
<img>
’s layout dimensions, which will update thesizes=auto
value, which may trigger a new resource load; the new resource will have new natural dimensions, which …&c.

sizes=auto
was just: people coming up with corner cases that would load every single resource in a srcset
, one-after-the-other, using this mechanism.Simon Pieters, who wrote the auto-sizes spec, made a few valiant and clever attempts to define all of the cases that could possibly trigger cyclical dependencies and work around them in ways that wouldn’t be too weird or noticeable for authors. But when the rubber hit the road and implementers started implementing, those workarounds didn’t hold up. Ultimately, ’twas decided that the only way forward was to make a clean cut: mandate that a loaded resource’s natural dimensions cannot affect its <img>
’s layout in any way, ever, when sizes=auto
is involved. Luckily, we have a fairly new feature that explicitly does exactly that: size containment.

So now, this is in Chrome Canary’s UA stylesheet:
img:is([sizes="auto" i], [sizes^="auto," i]) {
contain: size !important;
contain-intrinsic-size: 300px 150px;
}
That says: are we dealing with an <img>
with sizes=auto
? Then its natural dimensions are 300×150. NO EXCEPTIONS.
300×150 was chosen because that’s what <video>
and <canvas>
do; all of these elements use this smallish-but-non-zero default size in order to encourage you to do better.
Finally, we get to the load-bearing weasel-words in the title of this post: “sizes=auto
pretty much requires width
and height
attributes”. width
and height
are not the only way! I think the pattern that I’m pushing, where you describe the aspect ratio of your content in HTML, and give it a flexible size in CSS – provides a better separation of concerns than any other solution. But the fundamental takeaway is: when you use sizes=auto
, your image will be assigned a natural size of 300×150 and a natural aspect ratio of 2:1, and it’s on you to override those dimensions.