Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next/image not properly sizing images #44244

Open
1 task done
pjaws opened this issue Dec 21, 2022 · 20 comments
Open
1 task done

next/image not properly sizing images #44244

pjaws opened this issue Dec 21, 2022 · 20 comments
Labels
bug Issue was opened via the bug report template.

Comments

@pjaws
Copy link
Contributor

pjaws commented Dec 21, 2022

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 21.6.0: Thu Sep 29 20:12:57 PDT 2022; root:xnu-8020.240.7~1/RELEASE_X86_64
Binaries:
  Node: 18.12.0
  npm: 8.19.2
  Yarn: 1.22.19
  pnpm: N/A
Relevant packages:
  next: 13.0.8-canary.2
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

Image optimization (next/image, next/legacy/image)

Link to the code that reproduces this issue

https://github.com/pjaws/next-image-example

To Reproduce

With fill prop

  1. Create div with desired aspect ratio padding, i.e. padding-top: calc(2/3 * 100%);
  2. Add next/image as child of that div
  3. Add sizes attribute with desired breakpoints/widths
  4. Images are not requested at these sizes/srcset is always the same regardless of what's in sizes

Without fill prop, external image

  1. Create next/image with remote image, width and height props specified
  2. Image is sized correctly at large screen sizes
  3. Image is actually much larger at smaller screen sizes

Without fill prop, local image

  1. Create next/image with local image, no width or height specified
  2. Image is always served at maximum resolution

Describe the Bug

In almost every case, the image requested by next/image is larger than expected, resulting in unnecessarily large file sizes.

For images with the fill prop, the sizes attribute doesn't seem to actually do anything. In the screenshot below, images that should be around 396px are loading at 640px (1200px screen, 33vw image).

Screen Shot 2022-12-21 at 2 01 32 PM

For images without fill, instead of generating smaller files for smaller devices, the requested image is usually much larger than the specified width and height. In the screenshot below, an image that should be a maximum of 800x533px is being loaded at a width of 1920px on a screen 375px wide.

Screen Shot 2022-12-21 at 2 09 56 PM

This can also be seen on your own example page where the local Vercel image is set to 1000x1000px, but actually loads the full-size 1600px image at a device width of 375px.

Expected Behavior

"Always serve correctly sized image for each device"

I'm not expecting magic, but currently next/image is doing the opposite of what I want/expect. It would be nice to have some way to load smaller images at smaller screen sizes.

Which browser are you using? (if relevant)

Chrome 108.0.5359.124

How are you deploying your application? (if relevant)

No response

@pjaws pjaws added the bug Issue was opened via the bug report template. label Dec 21, 2022
@kerns
Copy link

kerns commented Jan 3, 2023

Nice catch and detailed reporting. 🙏

@Tobi-mmt
Copy link

Tobi-mmt commented Mar 9, 2023

I am facing the same issue. Are there any updates ?

@staaky
Copy link

staaky commented Mar 10, 2023

Are you considering the pixel density of your device? In your screenshot the srcset contains x modifiers. It's picking the 1920px 2x image because of pixel density.

If you use a sizes prop like 100vw it should give you a srcset with w modifiers for a responsive image. Even in that case you'd have to consider device pixel ratio. If the srcset is for example small.jpg 400w, large.jpg 700w on a 375px viewport with a pixel density of 2 it'll actually load the large image.

@prncss-xyz
Copy link

Same problem, with page router as well as app router (13.3.1).

@romainbriand
Copy link

Facing the same problem:

<Image src={/...} width={40} height={40} />

Source image is 200x200 pixels but generated srcset calls an image version sized 48x48 for 1x and 96x96 for 2x 🧐

@wzpfish
Copy link

wzpfish commented Jun 21, 2023

@romainbriand +1
<Image src={iconSrc} alt='' width={16} height={16} />
source image is 32x32 px but rendered size is 16x20, where is 20 come from?

@zakinadhif
Copy link

Currently having the same problem, source image is 720px in width, but rendered size is 750px in width -- messing with aspect ratio thus displaying rather blurry image.

@omergencoglu
Copy link

Facing the same problem. I'm getting lower score in PageSpeed Insights for the mobile version due to "Properly size images" opportunity.

@jmcoder1
Copy link

jmcoder1 commented Oct 2, 2023

I currently have the same problem, too

@williambout
Copy link

williambout commented Oct 3, 2023

I can repro as well. In my <Image> component I specify a width of 1500px and a height of 1000px but the 2x image is 3840px wide.

CleanShot 2023-10-02 at 23 05 49@2x

CleanShot 2023-10-02 at 23 03 29@2x

@Pedromigacz
Copy link

Pedromigacz commented Oct 6, 2023

I'm still having this issue on 13.5.1.
When the window is 2048px wide, it generates me a nice 2048px image, but if the window is 2049px, it will try to generate a 3840px wide image and timeout.
Here is my component

<Image
  src={HeroBackground} // https://651f9af212fba00008bba0ed--extraordinary-cucurucho-baaf57.netlify.app/hero_background.png source image is 4096x1868
  fill
  alt='Aerial view of New York'
  className='absolute top-0 left-0 h-full object-cover'
  placeholder='blur'
  priority
  style={{ objectFit: 'cover', maxWidth: '2048px' }}
  sizes='(max-width: 2048px)'
/>

Here's a permalink with the issue: https://651f9af212fba00008bba0ed--extraordinary-cucurucho-baaf57.netlify.app/

@cjimmy
Copy link
Contributor

cjimmy commented Oct 6, 2023

@Pedromigacz You're using sizes incorrectly. Look at examples, and MDN docs. It should be a media query, and then a size for the image, and the last value of sizes must not have a media query.
Assuming you want a full-width image at 2048px, I think this is what you mean

  sizes='(max-width: 2048px) 100vw, 100vw'

next/image will fetch a 3840px image because that's the next image breakpoint. See https://nextjs.org/docs/app/api-reference/components/image#devicesizes to custom define it in next.config.js

also, nit: src should be a string, and your naming of HeroBackground suggests that it's a component.
also, style={{ maxWidth: '2048px' }} constrains how the image is shown, not the resolution that it's fetched at.
also, if you use placeholder='blur' you need to include a blurDataURL for that to work (docs).

As to why it's timing out, that sounds like some other issue...

(sorry to spam everyone on this thread)

@jrnkng

This comment has been minimized.

@jrnkng
Copy link

jrnkng commented Oct 11, 2023

Are you considering the pixel density of your device? In your screenshot the srcset contains x modifiers. It's picking the 1920px 2x image because of pixel density.

If you use a sizes prop like 100vw it should give you a srcset with w modifiers for a responsive image. Even in that case you'd have to consider device pixel ratio. If the srcset is for example small.jpg 400w, large.jpg 700w on a 375px viewport with a pixel density of 2 it'll actually load the large image.

What actually happens when I pass a 100vw prop to sizes?

@hc0503
Copy link

hc0503 commented Nov 30, 2023

@pjaws , I have same issue, do you have any updates?

@GregorTB

This comment has been minimized.

@giovaborgogno
Copy link

setting style={{objectFit: "contain"}} works properly for me.

<Image src={src} width={500} height={500} style={{objectFit: "contain"}}></Image>

@hayyaun

This comment has been minimized.

@Ma-Kas
Copy link

Ma-Kas commented Aug 12, 2024

Same issue still present in 14.2.4

e.g. an Image component with the following:
sizes='(max-width: 1100px) 100vw, 1100px'

seems to correctly construct srcset when inspecting (albeit pointlessley up to the max breakpoint of 3840), but always loads the 1200 breakpoint version even on mobile.

@evejk
Copy link

evejk commented Aug 22, 2024

I had the same issue and after two days of pulling my hair out, I concluded that this is a feature and not a bug… (Although I’m not sure all reported issues here are due to the same cause...)

As someone above already mentioned:
The Image component takes DPR into account. So if your viewport is 200px width and DPR is 2x, it will try to serve the image closest to 400px. The browser will default to your machines' resolution, which in my case is 2x (MacBook).

You can easily test this in the dev tools when enabling the DPR dropdown and switching between 1/2/3 DPR settings.
You may need to enable the DPR setting first to see it. In Chrome/Arc it is not visible by default.
(After I tested the different settings and checked the network tab for the loaded image size, the world made sense again…).
CleanShot 2024-08-22 at 22 08 51@2x

CleanShot 2024-08-22 at 21 51 08@2x


If you enable an emulated device in the browser, it will use the appropriate DPR for that device. -->
Dimensions Samsung Galaxy S8+ y

I haven’t found a way to avoid this behavior besides just using a regular ol’ img-tag with x descriptors.

Another (overcomplicated) way might be to use the new-ish getImageProps() to create an image-set and tell it to use the same image size for all resolutions – but that's just an outlandish theory I haven't tested (yet).

Also: unless you set/override them manually in the .config, Next uses default deviceSizes and imageSizes to generate the srcSet which are:
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840]
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384]

-> which might explain why @romainbriand is getting 48x48 and 96x96 instead of 40/80 if sizes prop is set

-> which might explain why @zakinadhif is getting 750 instead of 720
-> which also explains the max breakpoint of 3840 @Ma-Kas

hope it helps :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests