Skip to content
Go back

Is sizes="auto" ready for prime time?

Published:

Responsive images are about to get a whole lot easier. Using sizes="auto" for lazy loaded images is supported in Chrome 126+. Apple and Mozilla have voiced their plans to support it.

Shopify has released a polyfill for this feature too! Now is a great time to try sizes="auto" on your next production project.

Life before sizes=auto

I’ve never enjoyed writing out the sizes attribute for responsive images. It’s a necessary hassle to build a well-performing site. But there’s so much context you, as a developer, need to keep in your head:

  1. How will my CSS & breakpoints affect the width of these images?
  2. If I change a breakpoint or grid layout, will I have to update every image on the site?
  3. Or even more likely… I flat out forget to add sizes until the very end of a project.
sizes="(min-width: 1440px) 1200px, (min-width: 768px) calc(100vw - 320px), (min-width: 640px) calc(100vw - 64px), calc(100vw - 24px)"

👆 That’s a lot of math.

Back before loading="lazy" was widely supported, I used the lazysizes library. One of its killer features was automatic sizes. Just plop in data-sizes="auto" and it would figure out the rest. However, now that lazy loading is a baseline browser feature, it doesn’t make sense to load up the entire library just for one feature (it doesn’t seem to be actively maintained either).

Well, guess what? Just like loading="lazy" of old, sizes="auto" is ready for prime time. It’s already available in Chromium 126+… which was released (checks notes) over a year ago!

But what to do about other browsers?

The Promise of sizes="auto"

Chris Coyier wrote about this back in 2023, and I’ve been patiently hoping it would gain traction. The idea is beautifully simple. If the image is lazily loaded, the browser has time to calculate the sizes value based on the actual rendered size of the image.

What if we could go from this (manually writing sizes):

<img 
  alt="A sea turtle swimming gracefully through crystal clear water."
  width="2000"
  height="1500"
  srcset="
    turtle-300.jpg  300w,
    turtle-600.jpg  600w,
    turtle-1200.jpg 1200w,
    turtle-2000.jpg 2000w
  "
  sizes="
    (min-width: 2420px) 2000px, 
    (min-width: 720px) calc(94.76vw - 274px), 
    (min-width: 520px) calc(100vw - 96px), 
    calc(100vw - 32px)
  "
  loading="lazy"
>

Adapted from Chris Coyier’s example

To using sizes="auto" (width and height are recommended):

<img 
  alt="A sea turtle swimming gracefully through crystal clear water."
  width="2000"
  height="1500"
  srcset="
    turtle-s.jpg  300w,
    turtle-m.jpg  600w,
    turtle-l.jpg  1200w,
    turtle-xl.jpg 2000w
  "
  sizes="auto"
  loading="lazy"
>

That’s it! No math, no media queries, no maintenance headaches.

Browser Support & The Shopify Polyfill

The sizes="auto" attribute is supported in Chromium 126+ (caniuse). That’s great for Chrome and Edge, but what about everything else?

Apple and Mozilla have both updated their standards position repos, signaling their support for this feature.

With all major browser engines on board, I’ve thought about writing a polyfill for a long while. However, the fine folks at Shopify beat me to it (and probably did a better job than I could). Their autosizes polyfill was released in May 2024, and it’s impressively lightweight. Coming in at roughly 160 lines of code.

How the Polyfill Works

Here’s the clever part, straight from their GitHub repo:

The polyfill uses mutation observers to detect newly added HTMLImageElement elements with loading=lazy and sizes=auto attributes.

It then removes any src and srcset attributes from those images, ensuring that they won’t start loading images.

Once First-Contentful-Paint is fired, which we use as a proxy to indicate that layout was calculated:

  • sizes attribute value gets overridden by the image’s getBoundingClientRect pixel size.
  • The attributes get written back as src and srcset, so that the image can load. Images that are changed or added after FCP fires get their sizes attribute rewritten without removing their src and srcset.

The polyfill backs off if the browser is too old to support FCP, or if it supports sizes=auto natively. Unfortunately, the latter information is only available through User-Agent sniffing.

Under the hood, it’s using the PerformanceObserver API to detect when First Contentful Paint occurs:

new PerformanceObserver((entries, observer) => {
  entries.getEntriesByName('first-contentful-paint').forEach(() => {
    didFirstContentfulPaintRun = true;
    setTimeout(restoreImageAttributes, 0);
    observer.disconnect();
  });
}).observe({type: 'paint', buffered: true});

Browser Support

Here’s the current state of browser support for sizes="auto":

Native Support:

(See caniuse.com)

With Polyfill:

See MDN: PerformancePaintTiming browser compatibility

Practical Implementation

If you’re using an image transformation service like Imgix, Cloudinary, or AWS serverless transforms, you can create reusable components that make responsive images trivial to implement.

React Example

function ResponsiveImage({ src, width, height, alt = "", className = "" }) {
  const sizes = [400, 800, 1200, 1800, 2600];
  
  // Generate srcset for Cloudinary (adjust for your service)
  const srcset = sizes
    .map(width => 
      `https://res.cloudinary.com/your-cloud/image/fetch/w_${width},f_auto,q_auto/${encodeURIComponent(src)} ${width}w`
    )
    .join(', ');

  return (
    <img 
      src={`https://res.cloudinary.com/your-cloud/image/fetch/w_800,f_auto,q_auto/${encodeURIComponent(src)}`}
      srcSet={srcset}
      sizes="auto"
      loading="lazy"
      alt={alt}
      className={className}
      width={width}
      height={height}
    />
  );
}

// Usage
<ResponsiveImage 
  src="https://example.com/turtle.jpg" 
  alt="A sea turtle swimming gracefully through crystal clear water." 
  width="2000"
  height="1500"
/>

Twig Example (Craft CMS)

I write a lot of Twig at work. This method works great for any framework or language.

{# _macros/image.twig #}
{% macro ResponsiveImage(props={}) %}
  {% set src = props.src %}
  {% set width = props.width ?? null}
  {% set height = props.height ?? null}
  {% set alt = props.alt ?? '' %}
  {% set class = props.class ?? null %}
  {% set sizes = [400, 800, 1200, 1800, 2600] %}
  
  {% set srcset = sizes | map(width => "https://res.cloudinary.com/your-cloud/image/fetch/w_#{width},f_auto,q_auto/#{src | url_encode} #{width}w") %}

  <img 
    src="https://res.cloudinary.com/your-cloud/image/fetch/w_800,f_auto,q_auto/{{ src | url_encode }}"
    srcset="{{ srcset | join(', ') }}"
    sizes="auto"
    loading="lazy"
    alt="{{ alt }}"
    width="{{ width }}"
    height="{{ height }}"
    {% if class %}class="{{ class }}"{% endif %}
  >
{% endmacro %}
{# In your template #}
{% from '_macros/image.twig' import ResponsiveImage %}

{{ ResponsiveImage({
  src:  'https://example.com/turtle.jpg',
  alt: 'A sea turtle swimming gracefully through crystal clear water.',
  class: 'hero-image',
  width: 2000,
  height: 1500,
}) }}

The Verdict? Ready for Prime Time!

After digging into the browser support and standards positions, I think that sizes="auto" is ready for production use.

What do you think? Are you already using sizes="auto", or do you have reservations about adopting it? I’d love to hear about your experiences - find me on LinkedIn.


Share this post on:

Previous Post
Mouse-free computing
Next Post
Ten Minutes