aem-core-wcm-components
aem-core-wcm-components copied to clipboard
[Image] Missing sizes attribute in v3
While we provide the srcset with all the renditions - mobile devices download images that are too large - https://pagespeed.web.dev/report?url=https%3A%2F%2Fwknd.site%2Fus%2Fen.html which results in longer time to render the page.
https://github.com/adobe/aem-core-wcm-components/blob/development/content/src/content/jcr_root/apps/core/wcm/components/image/v3/image/image.html#L29-L34
it's missing the sizes attribute
according to [1]: Source size values specify the intended display size of the image. User agents use the current source size to select one of the sources supplied by the srcset attribute, when those sources are described using width (w) descriptors.
[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset
Kudos to @Buuhuu and @joerghoh for pointing that out.
The challenge is that AEM doesn’t know what the size of the image element is, because that depends on the CSS that is applied, which isn't known by AEM. We can make an assumption in case the responsive grid is used by computing how many column wide the component is. However, this might still lead to loading too big images if the grid doesn’t take the full viewport width.
We can make an assumption in case the responsive grid is used by computing how many column wide the component is. However, this might still lead to loading too big images if the grid doesn’t take the full viewport width.
This may already be a reasonable implementation.
Alternatively you could default the sizes
attribute to the smallest slot size (smallest resolution, lowest quality). Using javascript you can determine the the actual width of the image's container and set the sizes
attribute accordingly. If that is combined with an intersection observer you can only load those images with the full resolution that enter the viewport. This will look like progressive decoding to the user.
It will cause some overhead as you may load the low resolution images unnecessarily. If you replace them with a single placeholder image 1x1 transparent pixel, this would work as well (if height and width is given it will normally scale with the right aspect ratio).
See siemens.com for example.
The way I read the specs, it seems we're supposed to describe in sizes
all available sizes from srcset
and the media queries for widths corresponding to those sizes. It's the browser's job to pick one of the sizes based on the available viewport and the provided media queries.
For example, if we define a set of allowed withs like [600,1000,1400]
, you would define the following:
sizes="(max-width: 600px) 600px, (min-width: 601px) and (max-width: 1000px) 1000px, 1400px"
@Buuhuu , am I missing something?
Exactly, you specify the media query to match for the desired slot size. The browser picks the first one that matches, so you don't need to specify ranges. In your example it should actually be
sizes="(max-width: 600px) 600px, (max-width: 1000px) 1000px, 1400px"
srcset="... 600w, ... 1000w, .... 1400w"
However, this takes the browser viewport width into account. For a 4 column layout that spans the entire viewport width and has no paddings/margins, you would not want to load the full width images. That's why an approach that takes the actual container size (incl. any css rules) into account will achieve better results.
Without javascript we could still guess based on the number of columns the image component spans (what @gabrielwalt described above). This should also be good enough.
So what's the alternative then? We define sizes="1px"
during server-side rendering and adjust the value to one of the available srcset
widths with Javascript? Siemens example for me seems to change sizes to arbitrary values (not specified in srcset
) and that causes the widest source to be used.
What the browser picks, is up to the browser after all. For example a browser may choose an image it already has cached even though the size is bigger than "needed".
There are some good articles about that, I am not a pro either - esp in the way the aem responsive grid works ;)
https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/#aa-being-more-chill-about-sizes https://observablehq.com/@eeeps/w-descriptors-and-sizes-under-the-hood
Has there been any update on this?
IMO the behaviour as it exist now makes V3 Image basically unusable. Sure, it's up to the browser which size it picks, and it would sure be nice if the browser picked sensible image sizes - especially when lazy-loading and it doesn't need to try to download it before the layout is known - but as it stands, without the sizes attribute the browsers assume 100vw - which is almost never correct.
It's closer to being correct on mobile phones, where usually the layout has collapsed down to a single column, and images usually (but not always) occupy most of that width. However, in desktop settings, the assumption of 100vw when the sizes
attribute is omitted is rarely correct.
Imagine if the content area of your site is restricted to 1200px, and is a 12 column grid. If an image is only 1 column wide then it will only render around ~100px - however depending on the size of the viewport it's entirely likely (even probable) that your browser is going to download the largest possible image.
Now imagine you have dozens or even hundreds of these images on a page - suddenly you are talking about dozens or hundreds of MB to load the page - which is the exact opposite to the intentions of responsive images.
The way policies work in AEM often requires me to specify a wide range of possible image widths, because while some images may be small, other images adhering to the same policy might be large. The result of this is that even images that are known to be small will download an extraneously large image (because the policy allows it, but never would have done it in V2 image).
I don't see any reasonable way to correct this server-side. Not only does the width depend on the styles, but even making a "better" guess based on column widths is likely to be incorrect.
I think browsers probably can do a better job (especially with lazy loading images), and they probably will in the future, but until that time I would prefer to go back to a JS based approach which is more likely to make a reasonable choice - even if it's not perfect.