Skeleton loading is a strategy/technique for improving user experience. In this post I want to share an example of how I would approach it without any UI libraries or fancy components.
Basically, skeleton loading is aimed at components or content areas that are being fetched from a backend or an API. We can use a loader for the entire page or even individual components, but this approach sometimes results in a flaky user experience. However, when applying skeleton loading we ensure that the basic structure of the page and its components are visible. Once our content is ready, we can remove the skeleton loaders and display the content.
Here is my skeleton loading example on codepen.io:
https://codepen.io/yossi_abramov/pen/jOqxOQp
A quick breakdown
For this example, I created a user card component that contains an avatar, name, email and contact button. The user card content is hard-coded for the sake of simplicity. In a real app or website you would probably fetch data and update the DOM
with it.
<div class="user-card skeleton">
<div class="user-cover">
<img class="user-avatar" src="
https://yossiabramov.com/images/avatar.jpeg" alt="user profile image" />
</div>
<div class="user-details">
<div class="user-name hide-text">Yossi Abramov</div>
<div class="user-email hide-text">josephabramov90@gmail.com</div>
</div>
<button class="contact-user hide-text">CONTACT</button>
</div>
Notice that the .user-card
element has a .skeleton
class and every element that contains text has a .hide-text
class.
Now, this example is a bit CSS heavy so letβs go over the most important lines:
/* Skeleton */
/* Static Skeleton */
.user-card.skeleton .user-cover {
background: #e2e2e2;
}
.user-card.skeleton .user-cover .user-avatar {
display: none;
}
.user-card.skeleton .user-cover::after {
content: "";
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
left: 0;
right: 0;
margin: auto;
bottom: -25px;
border: 1px solid #fff;
z-index: 10;
background: #e2e2e2;
}
/* Animated Skeleton */
.user-card.skeleton .hide-text {
background: #e2e2e2;
color: transparent;
position: relative;
overflow: hidden;
}
.user-card.skeleton .hide-text::before {
content: "";
position: absolute;
left: 0%;
top: 0;
height: 100%;
width: 50px;
background: linear-gradient(to right, #e2e2e2 25%, #d5d5d5 50%, #e2e2e2 100%);
animation-name: gradient-animation;
animation-duration: 2s;
animation-iteration-count: infinite;
filter: blur(5px);
}
@keyframes gradient-animation {
from {
left: 0%;
}
to {
left: 100%;
}
}
Basically, I have two states of skeleton loading: static and animated. The .user-cover
and .user-avatar
elements have a static skeleton β without any CSS transition or keyframe animation applied to them while all elements with the .hide-text
class have a keyframe animation. The gradient-animation
animation is applied to a ::before
element that is positioned absolute
to itβs relative
.hide-text
father. The animation is very simple but effective.
The JavaScript for this example only simulates a somewhat slow fetching of data. Once our data is fetched, we can remove our skeleton loaders.
const $el = document.querySelector(".user-card");
// Loading finished
setTimeout(() => {
$el.classList.remove("skeleton");
$el
.querySelectorAll(".hide-text")
.forEach((el) => el.classList.remove("hide-text"));
}, 3000);
Hope you find this approach to skeleton loading simple and clear π .
β For more posts:
https://yossiabramov.com/
Top comments (2)
Amazing! Thanks for sharing
I was looking for this from such a long time. Thank you brother!π€