photo of a skull
Photo by Mathew MacQuarrie on Unsplash

How to Build a Skeleton Loading Placeholder

Explore some basic strategies for creating skeleton loading placeholders.

4 min read

Today we're going to be creating a basic skeleton loading placeholder. This type of UI treatment can be found on most modern websites and mobile apps, and tells the user that the page, or a single element on a page, is in the process of loading some content. When used correctly, they are way to visualize what to expect before specific content is loaded, and can be far more visually pleasing than a loading spinner.

They are fairly easy to create, so let's start!

Markup

We want to create the underlying markup or "skeleton" that will determine what the placeholder should look like. In this example, we want ours to look like a card with an image and text beneath. In the HTML, we'll accomplish this with a series of divs, but you may use any markup you like.


<div class="placeholder shimmer">
  <div class="faux-image-wrapper">
    <div class="faux-image" />
  </div>
  <div class="faux-text" />
  <div class="faux-text short" />
</div>

Start by creating a wrapping div with classes of placeholder and shimmer. These will control the outer container of the placeholder and also a loading animation we'll add later.

Next, we create the placeholder "image" div with a class of faux-image-wrapper. This will contain another div with a class of faux-image.

Finally, we create two divs which will be for the placeholder text. One will be a bit shorter than the other, so we'll add an additional class to target the shorter one.
If you wanted to keep the amount of DOM elements minimal, you could replace a lot of the divs with before and after pseudo elements.

Styles

Now for the placeholder styles.


.placeholder {
  max-width: 300px;
}

We want to start by setting a max-width. For your use case, it will vary what the actual width should be. For example, if you're creating a grid of several placeholder items, you may want a percentage.


.faux-image-wrapper {
  margin-bottom: 10px;
  position: relative;
  padding-bottom: 100%; /* Or height: 200px; */
}

Next there is the faux-image-wrapper. We want the wrapper to be relatively positioned with a padding-bottom of 100%. What this does is create a flexible container for the faux-image div that is responsive in height and width. You could set a static height in pixels on this element, but it won't adjust quite as nicely. Experiment to see what works for you.


.faux-image {
  background: #dddddd;
  border-radius: 4px;
  height: 100%;
  position: absolute;
  width: 100%;
}

For the faux-image, we want the element to be absolutely positioned, with a height and width of 100%. This plays into the styles from the faux-image-wrapper, where we now have a responsive "image" that resizes depending on the width of the placeholder itself.


.faux-text {
  background: #dddddd;
  border-radius: 4px;
  height: 20px;
  margin-bottom: 5px;
}

.faux-text.short {
  width: 75%;
}

The faux-text has a background, border-radius, and a second shorter bit of text with a 75% width. We need to set heights on these text divs because they don't contain any actual content and otherwise would have a height of 0.

Shimmer Effect

At this point, you should have a loading placeholder. However, it's looking a bit dull. We want to really illustrate that some content or data is loading, and the way to do that is with animation.

This effect is accomplished with the shimmer class we added above to the placeholder div.


.shimmer {
  overflow: hidden;
  position: relative;
}

The base shimmer class is simple: we want it relatively positioned to contain any absolutely positioned elements transitioning on top of it, and we want to hide any overflow that might occur from that.

All of our effect styles are contained in a ::before pseudo element. This is the only slightly complicated piece of these styles, so let's take a closer look.


.shimmer::before {
  content: '';
  position: absolute;
  height: 100%;
  width: 100%;
  z-index: 1;
}

First, we want this element absolutely positioned and set to a height and width of 100%. We also want to set a z-index of 1, so that this element covers the absolutely positioned faux-image we created earlier.

Below we're temporarily adding a background color of red to better visualize what's happening. Once added, you'll see a red square covering the placeholder.


.shimmer::before {
  content: '';
  position: absolute;
  height: 100%;
  width: 100%;
  z-index: 1;
  background: red;
  animation: shimmer 1s infinite;
}

Now for the animation. We want the ::before element to transition across the loading placeholder from left to right, and to repeat this transition once every second.


@keyframes shimmer {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

When the animation begins (our 0% value), we want to our pseudo element position starting at -100%, essentially all the way to the left. By the end of the animation (our 100% value), we want it positioned all the way to the right at 100%.

Since we have the overflow:hidden property in place on our shimmer div, the pseudo element should be visually "clipped," and the animation shouldn't spill over to other adjacent items.

You should now see the red pseudo element transitioning from left to right.

temporary red shimmer background
temporary red shimmer background 


Now that we can see our animation is working, the last step is to change the red background to something that is more visually appealing.


.shimmer::before {
  ...
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0) 0%,
    rgba(255,255,255,0.4) 50%,
    rgba(255,255,255,0) 100%
  );
}

One way to accomplish this is by setting the background to a white linear gradient. This keeps the shimmer pseudo element from having a "blocky" appearance like we saw above.

And that's it! This skeleton loading placeholder technique can be applied in any scenario where you want to display more than a loading spinner to the user.