Creating a Skeleton Loader using JavaScript and CSS Tutorial

In this tutorial, you will learn how to Create a Simple Skeleton Loader for your websites or web applications data container using JavaScript and CSS. The tutorial aims to provide the IT/CS students, self-learners, or beginners with a reference for enhancing their knowledge and skills for developing a creative web application using JS and CSS. Here, snippets will be provided so you will have a better idea of achieving the goal of this tutorial. A sample source code zip file is also provided and is free to download.

What is Skeleton Loader?

Skeleton Loader is a kind of loader for information containers that are still loading or in progress. This is a static or animated placeholder that will be shown on the client side end while waiting for the data or information to be successfully loaded on the page. This loader also represents the view design of the data in the container whereas text fields, images, etc are presented with blank content and located as the data suppose to be.

Here's an example page interface that displays a skeleton loader:

Skeleton Loader Sample

Here's an example page interface when the data is/are successfully loaded:

Skeleton Loader Sample

How to Create a Skeleton Loader?

Skeleton Loader can be achieved using JavaScript and CSS. The CSS will handle the design and the animation of the loader while the JS will manipulate the content/information container before and after the data loads successfully.

Note, the skeleton loader design depends on how your actual data is being displayed on your websites or web applications. I will be only giving an example using a simple social media/network site for educational purposes only.

Example

Here are the example scripts for building a simple web page with a Skeleton Loader Feature. Aside from JS and CSS, I also used the Bootstrap Framework library for some of the page interface designs.

Stylesheet

The below code is a CSS Script for the custom design of the application post containers, skeleton loader, and other elements of the page interface.

assets/css/styles.css

  1. html, body{
  2.     min-height:100%;
  3.     width:100%;
  4.     background-color: #FBF8F1;
  5. }
  6. .truncate-3{
  7.     display: -webkit-box;
  8.     -webkit-line-clamp:3;
  9.     -webkit-box-orient: vertical;
  10.     word-break: break-all;
  11.     overflow: hidden;
  12.     hyphens: auto;
  13.     width:100%;
  14. }
  15. body>main{
  16.     height: 100%;
  17.     width: 100%;
  18.     display: flex;
  19.     flex-direction: column;
  20. }
  21. body>main>nav, body>main>footer{
  22.     flex-shrink: 1;
  23. }
  24. div#main-wrapper{
  25.     flex-grow: 1;
  26.     overflow: auto;
  27.     position:relative;
  28.     min-height: 70vh;
  29. }
  30. main>nav{
  31. background-color:       #0072b2 !important;
  32. }
  33.  
  34. main>footer {
  35. background-color: #f3efe6;
  36. }
  37.  
  38.  
  39. /**
  40. * Post Designs
  41. */
  42.  
  43. .user-img-container{
  44. width: 40px;
  45. height: 40px;
  46. border-radius: 50% 50%;
  47. }
  48. .post-img-single-container:not(:empty){
  49. width: 100%;
  50. min-height: 35vh;
  51. background-color: var(--bs-gray-200);
  52. padding: 0 5px;
  53. }
  54.  
  55. /**
  56. * Skeleton Loader Post Designs
  57. */
  58. .skl-load{
  59. position: relative;
  60. overflow: hidden !important;
  61. }
  62. .skl-load .user-img-container,
  63. .skl-load .text-col{
  64. background: var(--bs-gray-300);
  65. background-color: var(--bs-gray-300);
  66. }
  67.  
  68. .skl-load .text-col{
  69. width: 100%;
  70. min-height: 20px;
  71. margin:10px 0 ;
  72. }
  73. .skl-load .post-img-single-container{
  74. height: 100%;
  75. width: 100%;
  76. display: flex;
  77. align-items: center;
  78. justify-content: center;
  79. min-height: 35vh;
  80. background-color: var(--bs-gray-200);
  81. padding: 0 5px;
  82. }
  83. .skl-load .post-img-single-container:before{
  84. position: absolute;
  85. height: 150px;
  86. width: 150px;
  87. display: flex;
  88. background-image: url(./../img/img-icon.png);
  89. background-size: 150px 150px;
  90. content: "";
  91. opacity: .3;
  92. }
  93. .skl-load:after {
  94. content: "";
  95. position: absolute;
  96. height: 100%;
  97. width: 100%;
  98. background: linear-gradient(120deg, rgb(254 254 254 / 8%) 24%, rgb(255 255 255 / 32%) 50%, rgb(254 254 254 / 13%) 77%);
  99. transform: translateX(-100%);
  100. z-index: 2;
  101. animation: shine 1.5s infinite 3s;
  102. }
  103.  
  104.  
  105. @keyframes shine {
  106. 0%{
  107.     transform: translateX(-100%);
  108. }
  109. 100%{
  110.     transform: translateX(100%);
  111. }
  112. }

Page Interface

The below code is an HTML script that contains the different containers and elements of the application's page. The script contains a navbar, footer, and cards for post items.

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.     <meta charset="UTF-8">
  4.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>JS - Skeleton Loader</title>
  7.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  8.     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
  9.     <link rel="stylesheet" href="assets/css/styles.css">
  10.     <script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>
  11.     <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  12.     <style>
  13.         .material-symbols-outlined {
  14.             font-variation-settings:
  15.             'FILL' 0,
  16.             'wght' 200,
  17.             'GRAD' 0,
  18.             'opsz' 48
  19.         }
  20.     </style>
  21. </head>
  22.     <main>
  23.         <nav class="navbar navbar-expand-lg navbar-dark bg-gradient">
  24.             <div class="container">
  25.                 <a class="navbar-brand" href="./">JS - Skeleton Loader</a>
  26.                 <a href="https://sourcecodester.com" class="text-light fw-bolder h6 text-decoration-none" target="_blank">SourceCodester</a>
  27.             </div>
  28.         </nav>
  29.         <div id="main-wrapper">
  30.             <div class="container-md px-5 my-3">
  31.                 <div class="col-lg-6 col-md-8 col-sm-10 col-xs-12 mx-auto" id="posts">
  32.                     <div class="card shadow rounded-0 post-item mb-3">
  33.                         <div class="card-header bg-light">
  34.                             <!-- User Information -->
  35.                             <div class="d-flex align-items-center">
  36.                                 <div class="col-auto">
  37.                                     <div class="user-img-container position-relative overflow-hidden"></div>
  38.                                 </div>
  39.                                 <div class="flex-grow-1 flex-shrink-1 ms-2 user-name-container">
  40.                                 </div>
  41.                             </div>
  42.                             <!-- End of User Information -->
  43.                         </div>
  44.                         <div class="card-body rounded-0">
  45.                             <div class="container-fluid">
  46.                                 <div class="card-description truncate-3 post-description">
  47.                                 </div>
  48.                             </div>
  49.                         </div>
  50.                         <div class="card-img-top">
  51.                             <div class="post-img-single-container position-relative overflow-hidden post-img"></div>
  52.                         </div>
  53.                     </div>
  54.                 </div>
  55.             </div>
  56.         </div>
  57.         <footer class="shadow-top py-4 col-auto">
  58.             <div class="">
  59.                 <div class="text-center">
  60.                     All Rights Reserved &copy; <span id="dt-year"></span> | <span class="text-muted">JS - Skeleton Loader</span>
  61.                 </div>
  62.                 <div class="text-center">
  63.                     <a href="mailto:[email protected]" class="text-decoration-none text-body-secondary">[email protected]</a>
  64.                 </div>
  65.             </div>
  66.         </footer>
  67.     </main>
  68.     <script src="assets/js/skeleton-loader.js"></script>
  69.    
  70. </body>
  71. </html>

Sample Data

The following JSON Data are the sample information that will be loaded on the application page. It contains the list of posts' user information, details, and images path.

data.json

  1. [{
  2.     "username": "Mark Cooper",
  3.     "user_image_path": "./assets/img/man_avatar.png",
  4.     "post_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis sed odio dapibus maximus. Pellentesque rutrum nunc ac rutrum tempor. Nunc malesuada diam eget tempus pulvinar. Donec non sodales nibh, id auctor dui. Suspendisse nec dolor nisl. Phasellus in iaculis arcu, non euismod mauris. Sed non mi quis dui luctus vestibulum.",
  5.     "post_image_path": "./assets/img/smp-img.jpg"
  6. },
  7. {
  8.     "username": "John Smith",
  9.     "user_image_path": "./assets/img/man_avatar.png",
  10.     "post_description": "Aenean condimentum ornare sodales. Nunc pharetra vestibulum risus, ac luctus nunc consectetur imperdiet. Curabitur ultricies mollis neque, in iaculis dolor dictum a. Morbi imperdiet dolor venenatis dolor consectetur rhoncus consectetur sit amet urna. Mauris accumsan lorem eu justo vestibulum porta.",
  11.     "post_image_path": "./assets/img/smp-img-2.jpg"
  12. }]

JavaScript

The below code is the JavaScript file that contains the scripts for creating the container element of the sample data and applying a skeleton loader feature. In the script below, you can see that I used the setTimeout() function of JS to delay the process of showing the data on their designated containers.

assets/js/skeleton-loader.js

  1. // Loader Class Name
  2. const skeletonClassName = "skl-load"
  3. // Loader Text Fields default text-field
  4. const sklTxtField = `<div class="text-col w-100 h-100"></div>`
  5. // Posts Container
  6. const postsField = document.getElementById('posts')
  7.  
  8. // Clone Post Cart item
  9. const postItemClone = document.querySelectorAll('.post-item')[0].cloneNode(true)
  10.  
  11. // Posts JSON Data
  12. let postData;
  13.  
  14. // Retrieve Post Data
  15. fetch('./data.json')
  16.     .then(response => {
  17.         return response.json()
  18.     })
  19.     .then(responseData=>{
  20.         postData = responseData
  21.         // Add New Post Item if data is more than 1
  22.         Object.keys(responseData).map(k=>{
  23.             if(k > 0){
  24.                 postsField.appendChild(postItemClone.cloneNode(true))
  25.             }
  26.         })
  27.        
  28.         // Select all post items
  29.         const postItem = document.querySelectorAll('.post-item')
  30.  
  31.  
  32.         postItem.forEach((el,k)=>{
  33.             // Create a Skeleton Loader in post items
  34.             createSkeletonLoader(el)
  35.             setTimeout(()=>{
  36.             // Load the data
  37.                 loadData(el,k)
  38.             },6000)
  39.         })
  40.  
  41.         // Skeleton Creator Function
  42.         function createSkeletonLoader(postCard){
  43.             postCard.classList.add(skeletonClassName)
  44.             postCard.querySelector('.user-name-container').innerHTML = `${sklTxtField}`
  45.             postCard.querySelector('.post-description').innerHTML = `${sklTxtField}${sklTxtField}${sklTxtField}`
  46.         }
  47.  
  48.         // Post Items data loader
  49.         function loadData(postCard, dataKey){
  50.             var post = postData[dataKey]
  51.             postCard.querySelector('.user-name-container').innerHTML = `${post.username}`;
  52.             postCard.querySelector('.user-img-container').innerHTML = `<img class="img-fluid" src="${post.user_image_path}" alt="${post.username} Avatar">`;
  53.             postCard.querySelector('.post-description').innerHTML = `${post.post_description}`;
  54.             postCard.querySelector('.post-img').innerHTML = `<img class="img-fluid" src="${post.post_image_path}" alt="${post.username} Post Image">`;
  55.             if(postCard.classList.contains(skeletonClassName))
  56.             postCard.classList.remove(skeletonClassName)
  57.         }
  58.  
  59.     })
  60.     .catch(error=>{
  61.         console.error(error)
  62.     })
  63.    

Snapshots

As the result of the scripts that are provided above, the skeleton loader would be something like the following image:

Sample Application with Skeleton Loader

And after the data has loaded successfully, the skeleton loader will be changed into something like the following image:

Sample Application with Skeleton Loader

DEMO VIDEO

Please note that if you use the provided scripts and sample data above, change the image paths on the sample JSON data with the image paths that are available on your end. You can also download the full source code that I created for this tutorial so you will have an actual demonstration of the output of the provided scripts on your end. The download button is located below this tutorial's content.

That's it! That's the end of this tutorial. I hope that this Creating a Skeleton Loader using JavaScript and CSS Tutorial will help you with what you are looking for and will be useful for your current and future web application projects.

Explore more on this website for more Tutorials and Free Source Codes.

Happy Coding =)

Add new comment