Responsive placeholders for lazy-loading images


I was adding bits and pieces to a small project of mine (which I might write about in the future) when I came across an interesting problem. I had a bunch of images that were aligned in one row and resized using display: flex;, so that all of them would have the same height. This setup can be seen below.

These images were loaded lazily, which meant that the height of the page and the layout in general changed as more pictures were loaded. In this article I'll talk about creating responsive placeholder for images when you know the dimensions of the image beforehand.

Problem statement

As seen in the embed above, we have a row of responsive pictures. We want to replace these images with placeholders until loading is completed, and we want these placeholders to look and behave exactly like the original images. We want this because

We don't have any thumbnails available. We also want our solution to work correctly even when perfect alignment wasn't possible (seen below). This happens when the total width of all images is smaller than the width of the container, since we don't stretch our images.


The placeholder that satisfies our requirements can be built using two divs and some CSS. The basic concept is as follows:

<div class="placeholder-wrapper" style="max-width: {IMAGE_WIDTH}px;">  
    <div class="placeholder" style="padding-top: {RELATIVE_IMAGE_HEIGHT}%;">
        <img src="./image.png" />
.placeholder-wrapper {
    margin: 0 auto;
    padding: 0;
.placeholder img {
    max-width: 100%;

Where {IMAGE_WIDTH} is the original width of the image, and {RELATIVE_IMAGE_HEIGHT} is defined as IMAGE_HEIGHT / IMAGE_WIDTH * 100. Obviously, this requires you to know the dimensions of the image beforehand.

The reason we need two divs instead of one is that padding-top ignores any margin induced by margin: 0 auto;, which means that in cases when the total width of all images is less than the width of the container, our placeholder can behave incorrectly.


Applying this idea to our initial example:

If you found this post useful, feel free to like and share: