Understanding the Sizes Attribute and Next.js Image Component
Introduction
Next.js' Image component is well documented, and also confusing, but why?
One of the main reasons is because, under the hood, the <Image/>
component uses native HTML responsive images, which are confusing.
So when you take something that already confuses people, and then add a swathe of features on top to cover all the potential use-cases, you need a lot of documentation to help all the confused developers.
This post explains some nuances that may have left you feeling confused, without knowing why, or wanting to invest the time into finding out why things are the way they are.
HTML image
Here's an example of a native HTML image.
You need to at-least have a src
attribute with a value,
and for accessibility purposes,
a description of the image in the alt
attribute.
You can then enhance the experience by adding various sizes of the same image at different resolutions, this is called resolution switching.
If you add a width and height, and apply a dash of CSS to your images, they will also respect the aspect ratio of the image. The example below has an aspect ratio of 2/1.
Finally, you can add the sizes
attribute.
Providing you supply these attributes, the web browser takes the values and decides which image to serve based on the following criteria:
- viewport width
- width of media described by the descriptors, e.g.
400w
- device pixel density
In the case of the previous example, if the viewport width is less than 768px
, any of the images could be served.
If the devicePixelRatio
is 1
, and the viewport width is 400px
or less, goat-400.jpg
is served, however, if devicePixelRatio
is 2
goat-600.jpg
is served instead as it has more pixels.
Above a viewport width of 768px
I specified,
using sizes
, that the physical size of the image is 200px
,
that tells the browser to only get smaller images.
This complexity is what next/image
is built on top of.
Next Image component
Next's <Image/>
component uses some of the same props as <img/>
, here are the ones you'll mostly use:
Notice the missing srcset
attribute?
One of the convenient features of <Image/>
is that srcset
is taken care of for you,
automatically, based on what you provide via src
.
By default, if you supply a src
, width
and height
, this is the kind of srcset
that is output.
Due to the 1x
, 2x
descriptors,
the browser's decision about which images
to serve at different devicePixelRatio
is already made,
My preference
I'm not a fan of using 1x
, 2x
,
and prefer srcset
to contain a granular list of images at various sizes
appended by width descriptors e.g. 600w
.
I then use sizes
to inform the browser how big my images are at any given point,
the information allows the browser to pick the most optimal image.
The default behaviour with next/image
doesn't work like this,
and can only be enabled by using the sizes
attribute,
but produces some odd results.
My preference in the following example is to use sizes
,
enter a value of "280px, (min-width: 768px) 420px"
,
and get a srcset like this:
However, I get this:
I can't do anything to stop that from happening,
other than not using sizes
😭.
You might have never come across this, it seems like a hidden feature.
I stumbled upon it when I decided to use sizes
,
I didn't expect the vast array of images I got in the previous example.
The defaults cover a lot of bases, but I think this is an area for improvement. Imagine the HTML bloat on a busy product catalogue page, it's excessive, and completely unexpected.
The reason for the vast array of sizes is down the default configs
that come from deviceSizes
and imageSizes
combined.
They can be overridden in next.config.js
, but its a blanket rule,
I'd prefer to pass in a sizes value on a case-by-case basis,
and it be used as the value, overriding the default config.
How they must have intended this
Next.js provide documentation on how to use sizes
,
but it's an "either, or" solution,
and doesn't fit with how native responsive images work.
It's like they're recommending the default behaviour for the majority of cases,
and using sizes
is for other cases,
but due to the way it works it's sub-optimal.
We shouldn't have to add a random value like 50vw
to get a smaller srcset
,
that's just completely wrong.
This is from the docs:
The behaviour is unpredictable, and seems unintended, and there are a number of GitHub issues on the topic.
Conclusion
I'd prefer a single way to generate srcset
; using w
descriptors.
It would be more flexible, less confusing, and less of a surprise feature.
I'd also like it if,
when I use the sizes
attribute I get an array of images
that relate to the values used in sizes
,
and up-scaled versions of the same images at different resolutions.
Using sizes
should be a front and centre feature,
not something that feels like it's hidden away because it might confuse people,
the end result is even more complicated.
Written by Morgan Feeney
I’ve been designing and developing for almost 2 decades.
Read about me, and my work.
For juicy tips and exclusive content, subscribe to my FREE newsletter.