Insight Tech APAC Blog Logo

Loading Skeletons

Author:
Published: August 16, 2024

8 minutes to read

Overview

In the pursuit of faster and more responsive web apps, we have pushed an increasing amount of content loading onto the client. This paradigm shift to asynchronous, lazy-loaded content has improved perceived performance by reducing the Time to First Paint, while often introducing a new issue.

The Problem - Cumulative Layout Shift

Loading text, images and interactive web components after the page has begun to show can result in a phenomenon known as Cumulative Layer Shift (CLS). This is the “popping” and shifting of elements on the page that occurs as elements are updated and appended to the page.

On the surface, this may only look like a visual issue but CLS can negatively effect the user experience by shifting text and controls as that the user is actively using them. Google has gone as far as using CLS metrics in their page ranking for search results.

The Solution - Loading Skeletons

Thankfully, in many circumstances, we can avoid CLS with a simple placeholder element. Presenting the Loading Skeleton:

Do not adjust your set. Above was an example of the placeholder in action. As you can see, it’s a very simple visual element, serving two roles:

  1. Preventing CLS by reserving space where content will eventually be displayed
  2. Indicating to the user that part, or all, of the page is loading

Implementation

There are two parts to the styling of a loading skeleton:

  1. A shared CSS rule that is common across all loading skeleton elements on the page
  2. A per instance style to set the width & height, typically using an inline style defined by the project’s front-end framework
.loading-skeleton {
  display: inline-block;
  min-width: 0.5rem;
  min-height: 1rem;
  border-radius: 0.25rem;
  opacity: 0.5;
  background-color: #888;
}
<span
  class="loading-skeleton"
  style="width: 100px; height: 50px;"
></span>

“Advanced” Styling

For the most representative size and position of your loading skeleton, you should try to match the style rules that would apply to the content it represents.

If you’re using a CSS framework with utility classes, this often just means matching the classes. For example, if I was using Tailwind CSS then I might do something like this:

<!-- Loading skeleton -->
<span class="loading-skeleton w-24 h-24 mx-auto"></span>

<!-- Actual content it represents -->
<img class="w-24 h-24 mx-auto" src="/example.png">

If you are working with custom CSS rules, it might be easiest to separate any sizing-related styles out into their own rules so that you can reuse them on both the skeleton and actual content.

Use in Frontend Frameworks

Loading skeletons work great in frontend frameworks because they can be easily translated into components. Below are some examples of what that may look like.

Angular

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-loading-skeleton',
  template: `<span class="loading-skeleton" style="width: {{width}}; height: {{height}};"></span>`,
})
export class LoadingSkeleton {
  @Input() width = '5em';
  @Input() height = '1em';
}

React

const LoadingSkeleton = (props: { width?: string; height?: string }) => {
  return (
    <span
      className="loading-skeleton"
      style={{ width: props.width ?? "5rem", height: props.height ?? "1rem" }}
    ></span>
  );
};

export default LoadingSkeleton;

Examples

Below are a few example CSS rules that can be used as a jumping-off point for loading skeletons on your next project. They may need some minor adjustments to match the exact theme of the site but that should be fairly easy with this base.

Simple



.loading-skeleton {
  display: inline-block;
  min-width: 0.5rem;
  min-height: 1rem;
  border-radius: 0.25rem;
  opacity: 0.5;
  background-color: #888;
}

Pulse Animation



.loading-skeleton {
  display: inline-block;
  min-width: 0.5rem;
  min-height: 1rem;
  border-radius: 0.25rem;
  opacity: 0.5;
  background-color: #888;
  animation: LoadingSkeletonAnimation 1s ease-in-out infinite alternate;
}

@keyframes LoadingSkeletonAnimation {
  0% {
    opacity: 0.5
  }
  100% {
    opacity: 0.3
  }
}

Shine Animation



.loading-skeleton {
  display: inline-block;
  min-width: 0.5rem;
  min-height: 1rem;
  border-radius: 0.25rem;
  opacity: 0.5;
  background-color: #888;
  background-image: linear-gradient(100deg, #888 30%, #bbb 47.5%, #fff 50%, #bbb 52.5%, #888 70%);
  background-attachment: fixed;
  background-size: 100vw 100vh;
  animation: LoadingSkeletonAnimation 3s ease infinite;
}

@keyframes LoadingSkeletonAnimation {
  0% {
    background-position: -50vw 0
  }
  100% {
    background-position: 50vw 0
  }
}

Conclusion

With fairly little work, you can add loading skeletons as placeholder elements on your site. They will prevent CLS and greatly improve the user experience.