srcSet ignores sizes provided
@atcastle is already working on this.
Since Aug 4, 2021.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
What version of Next.js are you using?
11.0.1
What version of Node.js are you using?
14.15.4
What browser are you using?
Chrome Version 92.0.4515.107 (Official Build) (x86_64)
What operating system are you using?
macOS 11.5
How are you deploying your application?
Vercel
Describe the Bug
When I supply valid sizes, Nextjs ignores the intersection between these sizes and the deviceSizes & imageSizes contained in the next.config.js file. As a result, a file that will only ever appear on-screen at 280px (or 560px @ 2.0 pixel density, or 840px @ 3.0 pixel density) still generates every size, all the way up to 3840w.
Fortunately, the browser still requests the correct image for each screen resolution, but there are many sizes which are unreachable by any browser or device, and these are typically the largest, most resource-intensive ones to produce and maintain.
Expected Behavior
If provided, Nextjs should parse the sizes value to determine which deviceSizes & imageSizes are applicable to each image. Since both monitors & devices have a pixel density from 1.0 to 3.0 (or higher, though I recommend this as a sane limit), the smallest value in the sizes can be used as-is as a lower bound, whereas the largest sizes value should be multiplied by 3 to define an upper bound. Once these values are defined, they can be used as a floor/ceiling against the union of values in deviceSizes & imageSizes when generating scaled/optimized images. Values just outside of the target range may be worth generating, as well, so the browser has the flexibility to decide which image is most appropriate to request.
For example, given sizes="(max-width: 399px) 184px,(max-width: 519px) 244px,(max-width: 639px) 200px,(max-width: 767px) 156px,(max-width: 1023px) 220px,(max-width: 1279px) 280px,280px":
- The smallest image size (at 1.0 pixel density) is 156px
- The largest image size (at 1.0 pixel density) is 280px, but at 3.0 pixel density is 840px.
Given my current next.config.js include the following:
{
images: {
deviceSizes: [
144,
164,
184,
208,
234,
303,
358,
440,
488,
503,
524,
606,
640,
716,
750,
766,
828,
880,
1080,
1200,
1920,
2048,
3840
],
imageSizes: [
16,
32,
48,
64,
96,
128,
256,
384
],
},
}
...the smallest file size that needs to be generated is 144px and the largest is 880px. Everything outside of that range can be ignored for the generation of this srcSet. In addition to the smallest, note this includes:
- 1080
- 1200
- 1920
- 2048
- 3840
Each of these files takes time, processing & storage resources to generate and deploy. Eliminating them will deploy faster, require less processing, and require a smaller storage footprint. Additionally, the smaller srcSet will produce smaller HTML files, so they will download faster.
To Reproduce
Start with a source image provided at 3840px width and use import as recommended by the docs:
import missedTarget from '/public/images/missed-target.jpg';
Use next/image component with valid sizes smaller than the union of deviceSizes & imageSizes:
<Image
src={missedTarget}
layout='fill'
objectFit='cover'
placeholder='blur'
sizes={[
'(max-width: 399px) 184px',
'(max-width: 519px) 244px',
'(max-width: 639px) 200px',
'(max-width: 767px) 156px',
'(max-width: 1023px) 220px',
'(max-width: 1279px) 280px',
'280px']}
alt='missed target' />
Optionally, provide a next.config.js including the following:
{
images: {
deviceSizes: [
144,
164,
184,
208,
234,
303,
358,
440,
488,
503,
524,
606,
640,
716,
750,
766,
828,
880,
1080,
1200,
1920,
2048,
3840
],
imageSizes: [
16,
32,
48,
64,
96,
128,
256,
384
],
},
}
The rendered result will look like this:
<img alt="missed target" sizes="(max-width: 399px) 184px,(max-width: 519px) 244px,(max-width: 639px) 200px,(max-width: 767px) 156px,(max-width: 1023px) 220px,(max-width: 1279px) 280px,280px" srcset="/_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=16&q=75 16w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=32&q=75 32w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=48&q=75 48w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=64&q=75 64w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=96&q=75 96w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=128&q=75 128w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=144&q=75 144w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=164&q=75 164w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=184&q=75 184w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=208&q=75 208w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=234&q=75 234w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=256&q=75 256w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=303&q=75 303w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=358&q=75 358w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=384&q=75 384w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=440&q=75 440w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=488&q=75 488w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=503&q=75 503w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=524&q=75 524w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=606&q=75 606w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=640&q=75 640w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=716&q=75 716w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=750&q=75 750w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=766&q=75 766w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=828&q=75 828w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=880&q=75 880w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=1080&q=75 1080w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=1200&q=75 1200w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=1920&q=75 1920w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=2048&q=75 2048w, /_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=3840&q=75 3840w" src="/_next/image?url=%2F_next%2Fstatic%2Fimage%2Fpublic%2Fimages%2Fmissed-target.4cee4f8c3b9383517209b12b7cc81b00.jpg&w=3840&q=75" decoding="async" style="position: absolute; inset: 0px; box-sizing: border-box; padding: 0px; border: none; margin: auto; display: block; width: 0px; height: 0px; min-width: 100%; max-width: 100%; min-height: 100%; max-height: 100%; object-fit: cover; filter: none; background-size: cover; background-image: none;">
Note the srcSet includes all image sizes – all the way up to 3840 – regardless of whether they could possibly be displayed by any browser or device.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.