-
Notifications
You must be signed in to change notification settings - Fork 27k
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
Image component sizes
property does not work
#18413
Comments
I think it's only supposed to work if you use the srcset attribute. Not sure though. |
I thought of that too! But giving the fact that the Image component generates the |
I am experiencing the same. @SelvinM but the next/image already creates |
It's the intended size of the image so the browser can pick the best image from |
Makes a lot of sense, but still a little bit weird because the Image component creates a wrapper div with some element styles, and also styles the |
@josepholiveira Yep you're right with an unstyled raw image tag the sizes attr does control the width. I agree the wrapper divs + default styles do put you in a situation where you can't just drop in the new Image component without adding some extra styles. @Mistwell The Image component generates a |
I had the same issue but for now, as a workaround, I'm using the layout
In that way, the image won't shift the content and always respect the size of the container. Therefore, I would prefer to use the |
As @mrmcc3 said Next applies a list of device width breakpoints by default which is used to handle the cases where the images are wider than the viewport.
For instance:
The final length of this image will be smaller than 1200px in order to fit the viewport Refer to the
But if you want it to be fixed, regardless of
|
Next.js doesn't do any manipulation to the It is passed directly to the underlying The thing to remember is that Also, PR #19128 is going to autogenerate |
Can we get this re-opened? If I've understood correctly, I don't believe this is resolved. The image component is incredible and a brilliant idea, but it seems responsive images are not behaving as expected. When comparing @josepholiveira's example here -
To a pure html example, they should behave the same shouldn't they? Heres a codesandbox with both a next.js and html example side by side, trying to load the same size responsive images - export default function IndexPage() {
return (
<div>
<Image
width="1000"
height="571"
sizes="(max-width: 500px) 100px, (max-width: 1023px) 400px, 1000px"
src="https://i.imgur.com/3QPVI5K.png"
alt="Next.js"
/>
<img
srcSet="https://i.imgur.com/CLjcs9D.png 100w,
https://i.imgur.com/8Jqeo06.png 400w,
https://i.imgur.com/3QPVI5K.png 1000w"
sizes="(max-width: 500px) 100px, (max-width: 1023px) 400px, 1000px"
src="https://i.imgur.com/3QPVI5K.png"
alt="Next.js"
/>
</div>
);
} The html example is inline with the way the expected behavior quoted above by @josepholiveira, in that it is supplying a 100px image. The Next.js example loads a 320px image (as seen in network tab), and ignores the sizes request of a 100px image. (I'm also not sure why its 320, as that value is not present in deviceSizes or imageSizes). I know when using layout="responsive" next.js automatically supplies images that are sized based on the viewport, those images are at the following sizes: 640, 750, 828, 1080, 1200, 1920, 2048, 3840.. But is this ignoring of a requested size in the Maybe I am misunderstanding how responsive images or how the image component works, apologies if so. |
Here is another example of the sizes attribute not being used as expected - export default function IndexPage() {
return (
<div>
<div className="pic-wrap">
<Image
src="https://i.imgur.com/zrJVdrt.jpg"
alt="Next.js"
width="140"
height="210"
sizes="(min-width: 1024px) 140px, (min-width: 475px) 110px, 82px"
layout="responsive"
/>
</div>
<style jsx>{`
img {
width: 100%;
}
.pic-wrap {
width: 82px;
}
@media (min-width: 475px) {
.pic-wrap {
width: 110px;
}
}
@media (min-width: 1024px) {
.pic-wrap {
width: 140px;
}
}
`}</style>
</div>
);
} I am requesting an image that is
The biggest I would want is 140px wide image. Next.js image component is supplying a 320px (28kb) image, for all sizes. That is vastly bigger than needed. It also supplies a 640px when requesting a 82px wide image if i use layout="intrinsic": <Image
src="https://i.imgur.com/zrJVdrt.jpg"
alt="Next.js"
width="82"
height="123"
layout="intrinsic"
/> Why is this happening? A 640px image is vastly larger than an 82px wide image and will massively degrade performance. Again, apologies if I am misunderstanding how responsive images or how the image component works. |
@AshConnolly I've been dealing with a similar issue and I think I've figured some of this out. It's what @styfle said above. Basically the smallest image that Next generates by default is 640px. To allow Next to generate smaller images than 640px you need to create a
Everything after the specific sizes you are looking for are the Next defaults. Now you will have an image generated for each of those device widths. The |
Thanks for replying @cadekynaston! Appreciate it mate! 😃 That does seem to work, but it shouldn't be needed as next already has an imageSize array by default, and it contains images that are much smaller than the 320px that is being loaded -
this is taken from - https://nextjs.org/docs/basic-features/image-optimization#image-sizes
So it seems that next might not always be using this imageSizes array, and not be concatenating it with deviceSizes as explained. @styfle are you aware of this issue? Cheers! 👍 |
You're welcome @AshConnolly! Yea I thought the same thing. Couldn't get it to work with the |
This is because The |
Thanks for getting back @styfle! 😃 Interesting... so adding more sizes to deviceSizes should solve my As for my 2nd issue above with
Why is srcSet used for non-responsive images? Also imageSizes contains values much smaller than 320px, but I keep getting a 320px image returned when requesting a small image (sandbox example in my previous comment is 82px wide). Am I to believe that if i want to load a small, non responsive, image that is 48px wide (smaller than 320px which is often returned), I need to use That seems confusing and it doesn't seem to work - https://codesandbox.io/s/billowing-bush-n3m0n?file=/pages/index.js
So how would I go about getting the image component to return a small, non responsive image, at (for example) 48px width? As currently it keeps returning a 320px image. |
Thanks for replying @styfle.
Cool! I thought that seemed odd. Unfortunately my issue is still present. Can you clarify how would I go about getting the image component to return a small, non responsive image, at (for example) 82px width? As seen here - https://codesandbox.io/s/sweet-sanderson-fu2p0?file=/pages/index.js <Image
src="https://i.imgur.com/zrJVdrt.jpg"
alt="Next.js"
width="82"
height="123"
layout="intrinsic"
/> The component is returning a 320px wide image when asking for a 82px wide image. generated html: <img alt="Next.js" layout="intrinsic"
data-src="/_next/image?url=https%3A%2F%2Fi.imgur.com%2FzrJVdrt.jpg&w=320&q=75"
data-srcset="/_next/image?url=https%3A%2F%2Fi.imgur.com%2FzrJVdrt.jpg&w=320&q=75 320w"
style="visibility: visible; height: 100%; left: 0px; position: absolute; top: 0px; width: 100%;"
src="/_next/image?url=https%3A%2F%2Fi.imgur.com%2FzrJVdrt.jpg&w=320&q=75"
srcset="/_next/image?url=https%3A%2F%2Fi.imgur.com%2FzrJVdrt.jpg&w=320&q=75 320w"> 320px isn't even listed in I'd expect the component to return a 96px wide image as that is the closest larger value in imageSizes. On mobile devices with a 2x pixel density, if the component takes that into account (does it?), I'd expect a 256px image (the closest larger value, when the width is doubled for 2x pixel density). If you could clarify how to do this, I'd appreciate it! 😃 |
Hi @styfle! Thanks for replying! 😃 |
Thanks to @josepholiveira @styfle @AshConnolly & @LauraBeatris for your contributions here. I finally understand how to use the sizes prop with Next.js. Here's the simple explanation. Can I add this to the docs via a separate pull request? ---------------------- Need a smaller size? Just specify a smaller visual width. The default is 100vw, meaning 100% of the viewport. For a 3-column layout, try 33vw -- the closest image to 33% of the viewport will get served. You can also use media queries to get specific with your responsive image sizes. Don't forget to add additional deviceSizes in next.config.js if you need sizes <640px for mobile devices. Otherwise, 640px will be the smallest size Next.js can serve. For convenience, you can specify a custom deviceSizes by simply adding the default imageSizes array (which goes down to 16px) before the default deviceSizes.
Once you add that code, you can conveniently use any small vw value (like 10vw) and know that there will always be an appropriate version on tiny mobile devices. The smallest served will be 16px, and you'll have a total of 17 responsive image sizes served automatically by Next.js v10 or later. ---------------------- And here's a bunch more info in case anyone finds this issue while searching online like I did. Here's an example that can be used with this Next.js + Tailwind CSS starter blog https://jamstackthemes.dev/theme/nextjs-tailwind-starter-blog That starter blog has a nice overview of using Images in Next.js 10 here: https://tailwind-nextjs-starter-blog.vercel.app/blog/guide-to-using-images-in-nextjs Unfortunately it's not clear what to do with a layout="fill" image that won't fill the entire viewport, without reading the MDN docs several dozen times. (This obvious "use a sizes value of 33vw for an image in a 3-column grid" example does not appear in the MDN docs.) Here's a basic example of a small responsive image using 33vw (33% of the visual width of the device's viewport): index.js
Here's a full 3-column gird layout example that also has a long description of how you can use media queries in the string you pass as the "sizes" prop: index.js
And here's my Next.js config file: next.config.js
Hope this helps someone. And @styfle if you approve I'll open a pull request to make the changes to the docs. I don't want to do it right this second, because I could be entirely wrong here 😅 |
If you open a PR, we’ll take look 👍 I would also recommend a PR to MDN, since “sizes” is a web standard 🙂 |
I think this is still not fixed. I have the same issue. I'm simply trying to achieve From next.js perspective, I want something in the middle of From the docs:
The problem is, while using So, how can I achieve something in-between? Expected Result:
If it's supported already, can someone guide me? If not, it's better to open this issue? See Issue live on codesandboxThis should render at 200px maximum. |
Would the following be an acceptable approach for your case? <div style={{ maxWidth: 200 }}>
<Image width={200} height={200} layout="responsive" src="/pc.jpg" />
</div> |
Not sure, I exclusive use
1) wrapper div with fixed height and width (either fixed pixels or
determined by grid)
2) inner wrapper div with display relative
3) Next image with object-cover CSS class and layout="fill"
I put 2 and 3 as a CustomImage component so I can use it anywhere and
determine my sizing exclusively in 1. Works great for me.
I can't quite figure out what layout=responsive is for, but I think that's
the one for a full size image you want to scale up and down.
https://nextjs.org/docs/api-reference/next/image
Mine works fine for full size images as well, but images are rarely full
size on the web.
One other tip is that CSS background-image won't get optimized, so you have
to use CSS to absolute position elements on top of Next/image elements.
Which is a little tricky at first but also this Next/image is my favorite
tool I've ever used on the web (Tailwind CSS is second), so it's worth it.
… |
@AlexandraKlein @DoctorDerek we can make it work using a wrap div with fixed width. But don’t you think it should be supported by default since it's already adding two wrapping divs? Why should we bloat the code again with another div? |
I find page speed optimization much more important than code structure
that's hidden from the user. Ideally it would work even better with less
div wrappers for sure 😃 But it's still fantastic to drop it in to a
dynamic 3 columns grid layout at any device size with perfect performance.
|
I understand, My question is can't they support it by default? I feel it's broken for this case. maybe an additional props like |
I mean, it's the CSS implementation of object-fit / object-cover & the box
model that's "broken" because you have to play by specific rules to get CSS
to figure out width & height in advance if they're not specified directly
on the image.
And since it's incredibly easy to wrap a custom component around another
React Component, you could implement that feature yourself if whatever way
makes most sense for your use case without next.js have to force users into
certain use cases.
I totally agree that it's annoying to have to mess around & add a bunch of
<div>s just to have a proper "dynamic" image for the use cases I prefer,
and some examples in the docs would be great.
… |
@DoctorDerek, I agree. One could add any CSS rules they like in their own custom Image component. Ex: import Image from "next/image";
const CustomImage = ({ preserveMaxWidth, width, height, ...props }) => (
<div style={{ maxWidth: preserveMaxWidth ? width : "none" }}>
<Image
width={width}
height={height}
layout="responsive"
src="/pc.jpg"
{...props}
/>
</div>
);
export default function IndexPage() {
return (
<CustomImage
preserveMaxWidth
width={200}
height={200}
layout="responsive"
src="/pc.jpg"
/>
);
} |
Im using But everything else needs to be reconsidered by the Next team. This component is almost impossible to figure out. |
Whats interesting is
But 'px' doesnt:
And even then it doesnt make sense because an image size of 33vw is bigger on mobile than on desktop... |
The vw units aren't fixed or known in advance; they're like % units...
I'm still doing great with my "position: relative" div wrapper around a
"next/image" with "object-fit: cover" ... <div className="relative w-36
h-36"><Image src={image} alt={"The alt text"} layout="fill"
className="objectCover" /></div>
|
@DoctorDerek -> thats why its weird that vw even has an impact and pixels dont. Also your above recommendation does not seem to have any effect on my images.
The url that gets rendered out is:
|
Hey @gregg-cbs
Yes that is what I've found for imageSizes / deviceSizes as well, and I
think it's the intended behavior.
I'm surprised you didn't get the result you wanted from the "position:
relative" wrapper with "object-fit: cover"...
Though typically my production code uses a third wrapper that's display:
grid and give the width and height there.
With what you gave, you shouldn't need the ImageWrapper, because you
specified a width and height to the image.
Would it help if I sent over a CodeSandbox as an example of how I use the
dynamic sizing?
|
@DoctorDerek I wouldn't mind having a look at your dynamic sizing, would be nice for this thread to have all bits and pieces consolidated here. |
For sure, thanks for filling me in on what's working for you @gregg-cbs in
the spirit of helping the next person confused by this excellent feature of
Next. Cheers
… |
Ohhh good to know. Isn’t deviceSizes a little counter intuitive name as it not only relate to device size but more the size matching the media query? |
Yes but I seriously doubt they'd change it for clarity since it would be a
breaking change 🦥
|
True but they manage deprecation pretty well (exemple trailing slashes) I’m not sure to understand why every layout but responsive and fill are the use the concatenation of the two arrays (looking at the code)? |
Really? I exclusively use layout=fill and thought both arrays were being
combined. Could be wrong tho
|
In the merge talked about: next.js/packages/next/client/image.tsx Line 84 in 43d5cbd
in master it depends of the sizes attribute: next.js/packages/next/client/image.tsx Line 144 in 4fbf961
If you have any vw rule in sizes it takes everything in all arrays that is above the smallest deviceSizes * your smallest vw % Could not understand this either. Maybe I’m reading it wrong? |
@neckaros Yeah seems like a bug to me, layout=fill could be used for full-width but is used for any object-cover use case. Shouldn't it be if (
typeof width !== 'number' ||
// layout === 'fill' || // remove this line ?
layout === 'responsive'
) {
return { widths: configDeviceSizes, kind: 'w' }
} Seems like an easy fix 😀 Source: next.js/packages/next/client/image.tsx Lines 162 to 168 in 4fbf961
|
I agree the image tags are not documented very well and it is abit fustrating to have to wrap the image in a div all the time to do resizing in the styles this video really helped me https://www.youtube.com/watch?v=7fqKzvjQj94 |
This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Bug report
Describe the bug
When using the Image component, the
sizes
property doesn't work as described in the docs.When using the Image component, the image is resized to match the container width which is size of the
width
attributes that we passed, or fit 100% of the viewport if the image is larger than the screen, ignoring thesizes
attribute completely.Example of code:
To Reproduce
I created a codesandbox example with the exact problem:
width 800px
andheight 456.8px
sizes
prop that should tell the image to be100px
Expected behavior
I expect the image to have exactly a 100px when the view port is smaller then 500px, respecting the sizes attribute.
Screenshots
Bigger viewport width
Smaller viewport width
System information
The text was updated successfully, but these errors were encountered: