'Next.js Image- how to maintain aspect ratio and add letterboxes when needed

I have an area of my Next.js app which is the selected photo of a photo gallery so it has to stay fixed in size as people flip through the selected image or a photo is loading. I have a responsive layout but if really pressed, I'd say that this pixel area is 566px*425px.

I'm confused about how to actually do this. Here's the closest I've been able to get it, but the problem is that I get overflow of the image when the aspect ratio exceeds 566x425 and for images that have an aspect ratio below 566x425 it will stretch it in the Y direct. What I really want is to have a fixed box and then if the aspect ratios differ from the max size, you'll see letterboxes either along the sides or on the top and bottom.

           <div
            style={{
              position: 'relative',
              width: '566px',
              height: '425px',
            }}
          >
            <Image
              src={currCommit.image.url}
              alt="Current Image"
              layout={'fill'}
              objectFit="cover"
            />
          </div>


Solution 1:[1]

Ooh I got it! The key was to set the parent div to a fixed size and relative and then set the Image to a layout of fill and an objectFit of contain. The only downside to this approach is I need to set media queries so it will scale for smaller sizes.

<div className="relative item-detail">
  <Image src={currCommit.image.url} alt="Current Image" layout={'fill'} objectFit={'contain'} />
</div>

Then in the css I set:

.item-detail {
  width: 300px;
  height: 225px;
}

Solution 2:[2]

there's better solution i think, NextImage have callback property onLoadingComplete:

A callback function that is invoked once the image is completely loaded and the placeholder has been removed.

The onLoadingComplete function accepts one parameter, an object with the following properties: naturalWidth, naturalHeight

you can use the natural properties to set image ratio without loosing NextImage's layout functionality like this:

const NaturalImage = (props: ImageProps) => {
  const [ratio, setRatio] = useState(16/9) // default to 16:9

  return (
    <NextImage
      {...props}
      // set the dimension (affected by layout)
      width={200}
      height={200 / ratio}
      layout="fixed" // you can use "responsive", "fill" or the default "intrinsic"
      onLoadingComplete={({ naturalWidth, naturalHeight }) => 
        setRatio(naturalWidth / naturalHeight)
      }
    />
  )
}

the only downside is the aspect ratio applied only after image loaded, so the placeholder using the default ratio (in this case 16:9 - common), and this can cause CLS

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Coherent
Solution 2