How can I use next-image to scale images down to "max-width" and "max-height"?
Asked Answered
A

1

8

I have a blog that uses images with different scales and aspect ratios. Some are short and wide, some are tall and narrow. You can see an example here.

I want my images to:

  • Be at most as wide as the post (no larger than 100% width of their container), so that when an image is wide and short, it fills 100% of the width of the post.
  • Be at most 350px tall, so that when an image is tall and narrow, it's size is restricted by its height - it will be 350px tall, and centered in the middle of the post.

Like so:

To accomplish that with a regular img tag I use the following css:

  img {
    display: block;
    max-width: 100%;
    max-height: 350px; // to restrict the height of tall narrow images
    margin: auto; // to center the tall narrow images when their width is less than 100%
  }

Now I want to automatically scale down and optimize my images with next-image, but I can't seem to find the right combination of styles and parameters to accomplish the same effect.

When I set layout="responsive", the image always fills 100% of the width of the container, ignoring the max-height paremeter. So if the image is very tall and very narrow, it'll still get stretched to the full with of the post, making it ridiculously tall.

Like so:

When I set layout="fill" objectFit="contain" I can wrap the images in a container and set container's css to width:100%; height: 350px. Now it will scale down the tall narrow images to have at most 350px height, but if the image is very wide and very short, the container will still have the height of 350px (even though it should be much shorter now, because the image is short), which creates vertical space around the image.

Like so:

Can you help me to figure out how to solve this? What should I do to have the equivalent of the css above with next-image?

Accommodation answered 6/11, 2021 at 19:0 Comment(3)
There is probably some issue with your implementation. I am getting the desired result (what you've shown in the very first image) with just layout="fill" objectFit="contain". Here is a CodeSandbox.Determine
@CodeSandbox - your first and last images showcase the problem. Even though the images are vertically short, there are horizontal gray bars above and below each image, their container takes up the same amount of space. That's what I'm struggling with. If these images were embedded in the post, they'd have a lot of empty space around them (the gray bars).Accommodation
Yeah I see that now. Do you know the size of your images beforehand, i.e. can you use layout other than fill? If yes then you can do something like this: codesandbox.io/s/quirky-smoke-93jhw?file=/index.cssDetermine
A
9

It turned out to be surprisingly difficult to accomplish, but I did it.

function img({ src, width, height }) {
  const style = { paddingBottom: `min(350px, ${100 / (width / height)}%)` }
  return (
    <div className={`next-image-wrapper`} style={style}>
      <Image className="next-image" src={src} layout="fill" objectFit="contain" />
    </div>
  )
}

I use the next-image-wrapper to set the correct image size and proportions, and then use layout="fill" objectFit="contain" to tell next-image to make the image fill the wrapper and do its magic.

next-image-wrapper's width is always 100%, filling the container.

Padding is used to set the height of the wrapper.

For wide and short images, the padding maintains the aspect ratio of the image, making its height proportional to its width. The formula (100 / (width / height)) uses the original image's width and height to calculate how large the padding is supposed to be to keep the aspect ratio.

For the tall narrow images, I use css min() function to make sure that the padding calculated above never exceeds 350px. That way the padding remains proportional as long as its below 350px, and then it becomes 350px. At that point, next-image will automatically fit the image inside the box of this height.

In order to get the width and height of the original image I use rehype-img-size plugin when I process my mdx files as explained here.

Accommodation answered 8/11, 2021 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.