nextjs images - fill parent container while preserving original aspect ratio

using typescript and styled components

next/image is a powerful component that handles compression and caching for you.

that means - faster load times with smaller payloads.

getting the image to fill the parent container while preserving the original aspect ratio is a bit complicated, so here's a quick guide to help you with that!

the simplest case

by simply adding a parent component and setting the position to relative, the image will fill the space of the width and height set on the parent component.

const ImageContainer = styled.div`
    position: relative;
    width: 100px;
    height: 100px;
`;

<ImageContainer>
    <Image 
      src="placeholder.png"
      layout="fill"
      alt="placeholder image"
    />
</ImageContainer>

Rendering multiple images with different aspect ratios

this is where things get a bit complicated. in our blog site, we have a lot of images of different sizes. the ideal solution is to update all the images to unify the aspect ratio, but when you have a lot of content this can be difficult.

for this to work, you need to know the width and height of the original image. thankfully, in our case, our CMS contentful provides these.

the solution noted below allows us to have the images fill the height of the container while dynamically setting the width. the same pattern can be used to fill the width and have the height overflow and be hidden.

const CONTAINER_HEIGHT = 100;

const ImageContainer = styled.div`
    position: relative;
    width: 100px;
    height: ${CONTAINER_HEIGHT}px;
    overflow: hidden;
`;

const renderBlogImages = (articles: Article) => {
    articles.map((article) => {
        const imageWidth = article.image.width;
        const imageHeight = article.image.height;
        const ratio = (imageHeight) / CONTAINER_HEIGHT;
        return (
            <ImageContainer>
                <Image 
                  src="placeholder.png"
                  layout="fixed"
                  width={imageWidth / ratio}
                  height={CONTAINER_HEIGHT}
                  alt="placeholder image"
                />
            </ImageContainer>
        );
    });
}

const blog = () => {
    return (
        <FlexRow>
            {renderBlogImages(articles)}
        </FlexRow>
    )
}

Center images that overflow

Using the implementation above allows you to overflow either the width or height of an image to properly fill the container. This will cause images to be cut off and seem off-center.

By adding this css to the ImageContainer we can ensure the image stays centered

const ImageContainer = styled.div`
    position: relative;
    width: 100px;
    height: 100px;
    overflow: hidden;
    & span {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
`;

the next/image component renders a span wrapping the img tag, so we target the span in the ImageContainer with these styles, which ensure the image is centered.

✌️