Build Skeleton Screen Animations with CSS
Tags: css, webdev, animation
Published: 2022-12-05 23:13:08

by [Ejiro Thankgod](https://blog.openreplay.com/authors/ejiro-thankgod)



Recently, the web underwent advantageous changes for both users and developers. User experience is a broad topic that goes beyond current measures because new strategies for improving user experiences continue to develop.

One element of user experience improvement is skeleton displays. This tutorial explains what skeleton loaders are, why they're important, and how to make one for your website using React and basic CSS.

Skeleton displays are one component of UX improvement. The purpose of skeleton loaders, their significance, and how to create one for your website using React and basic CSS are all covered in this post.


## What is a Skeleton Screen?

While data is being loaded, a website's design is simulated by an animated placeholder known as a "skeleton screen." Additionally, and more importantly, they let the user know whether an image, text, card, or other information is loading while also letting them know that some content is loading.

This gives the user the impression that the page is loading more rapidly since they know the type of content loading before it manifests. We're referring to perceived performance here.

Here are some illustrations of skeleton displays from Youtube and Facebook:

![1 Youtube loading state](https://blog.openreplay.com/images/build-skeleton-screen-animations-with-css/images/image01.png)



![2 Facebook home feed loading state](https://blog.openreplay.com/images/build-skeleton-screen-animations-with-css/images/image02.png)


You can see the gray shapes representing how the data will be displayed after loading it. Once the server's data arrives, the screen's gray forms are replaced.

Skeleton loaders rarely increase productivity or loading speed. Their primary purpose is to give website visitors something to look at and the impression that the website is loading more rapidly.


Why are skeleton pages better, then?

- They appear to move faster and are simpler to use, and improved perceived performance offers a better user experience and higher conversion rates.
- The issue with spinners and loaders is that we have no idea what is loading or how long it will take.
- Using spinners or loaders places the user in a condition of uncertainty because the load time is unknown.
- Users are more interested in progress than they are in passing the time on skeleton screens.
- It provides the impression of being speedy and loading quickly.

And when should we use them?
- When numerous elements are loading at once, utilize to notify users that something is loading.
- When loading times for data exceed three seconds.
- When you have a popular website.
- When there is a drawn-out or background process.





## Build a Skeleton Loader with React

For the tutorial in this post, we'll create a simple page that lists articles for a certain blogger as cards. The finished item will look like this.

The Skeleton Loader Screen:

![3](https://blog.openreplay.com/images/build-skeleton-screen-animations-with-css/images/image03.png)


The Actual Site After Loading:

![4](https://blog.openreplay.com/images/build-skeleton-screen-animations-with-css/images/image04.png)

## Setting up React Project

We'll begin by putting up a React project with the code below to set up a skeleton loader in our React application:
```
npx create-react-app skeleton-react
```
With the following code, the working directory is changed to the folder created by the preceding command:
```
cd skeleton-react
```
Open the folder in the text editor of your choice. Our app is now configured; therefore, it's time to add and delete any extraneous boilerplate. We want to create a `components` folder in the `src` folder and a `server` folder in the root directory.

We have a `server` folder because we're going to build something that looks like a database to store data regarding the blogs. Don't worry; we won't be accessing any backend. We'll construct a fake REST API using `JSON` Server.

As a result, let's create a file called `db.json` or `data.json` in the `/server` folder and fill it with various data that you may commonly find on a blog, like a thumbnail, name avatar, title, description, and author.

Our `db.json` file should have the following information in it:

*N/B: Due to its vast size, the data is different from what I used in this article, but you are free to modify it as much as you like.*
```javascript
//db.json

{ "blogs" : 
[
    {
        "title" : "All about the JSON format",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_25gs97.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "Top alternatives to Create-React-App",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_Z1VLsJY.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "A Top Collection of CSS Code Generators",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_DRmM0.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "Top Visual Studio Code Extensions for React developers",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_Z8migj.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    
    {
        "title" : "Build a full-stack application with Amplication",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_Z5C3BB.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "Inheritance in JavaScript object-oriented programming",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_Q0pgi.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "Drag-and-drop events in JavaScript",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_Z1t0Y5y.webp",
        "description" : "Nov 24, 2022, 5 min read"
    },
    {
        "title" : "The best no-code development tools for developers",
        "thumbnail" : "https://blog.openreplay.com/assets/hero_14pmEO.webp",
        "description" : "Nov 24, 2022, 5 min read"
    }
]
}
```

We need to watch the above file using the JSON Server package so that we can add endpoints to it and conduct operations like POST, DELETE, GET, etc.

Launch a new terminal and type `npx json-server -watch server/db.json` `--``port 8000` to accomplish that. The terminal should launch the server on port 8000 and monitor any changes if it runs correctly. The JSON file has been hosted on localhost, as can be seen if you open `http://localhost:8000` in a browser. You should also visit `http://localhost:8000/blogs` to view the blog's resources.



<h2>Open Source Session Replay</h2> 

_<a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a> is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![OpenReplay](https://blog.openreplay.com/banner-blog_1oYPGT.png)

_Start enjoying your debugging experience - <a href="https://github.com/openreplay/openreplay" >start using OpenReplay for free</a>._




## Create a Fetch Function in App.js

Let's get the information from our local server first. Once the data has been successfully retrieved and any fetch problems have been handled, the data will be displayed using a template we will produce.

In our app.js

```javascript
import { useState, useEffect } from 'react';
import Blogs from './components/blogs';
import Skeleton from './components/skeleton';
const App = () => {
    const [blogs, setBlogs] = useState(null);
    const [error, setError] = useState(false);
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        setTimeout(() => {
            fetch(' http://localhost:8000/blogs')
            .then(response => {
                if(!response.ok){
                    throw Error('Sorry, some error occurred while fetching your blogs.');
                }
                return response.json();
            })
            .then(data => {
                setBlogs(data);
                setLoading(false);
                setError(false);
            })
            .catch(err => {
                console.log(err.message);
                setError(true);
            })
        }, 4000)
    })
    return(
        <div>
            <img src='/src/assets/open3.png'></img>
            {blogs && <Blogs blogs = {blogs} /> }
            <div className="container">
                {loading && [1,2,3,4,5,6,7,8,9,10,11,12].map((n) => <Skeleton key={n}/>)}
            </div>
            {error && <div className='container'><span className='error'>Error connecting to the server. Connection failed.</span></div>}
        </div>
    )
}
export default App;
```

## Create Blog and Skeleton Components

In the blogs, we will develop the `blogs.jsx` and `skeleton.jsx` components. We pass `blogs` as a prop and then generate templates for its various properties, such as `blog.description` and `blog.title`. In the Skeleton components, we developed a function that returns a `div` that arranges our `blog post`, `avatar`, `title`, `author`, and other elements to appear in the proper order.

```javascript
//blogs.js
const Blogs = ({blogs}) => {
    return(
        <div className="container">
            {blogs.map(blog => (
                <div className="blog">
                    <main className="image">
                        <img src={blog.thumbnail} alt="black screen with code over it" />
                    </main>
                    <footer>
                        <p className="text">{blog.date}</p>
                        <p className="title">{blog.title}</p>
                    </footer>
                </div>
            ))}
        </div>
    );
}
export default Blogs;
```

```javascript
//skeleton.js

const Skeleton = () => {
    return(
            <div className="skeleton-blog">
                <main className="skeleton-image">
                </main> 
                <footer className="skeleton-footer">
                </footer>
            </div>    
);
};
export default Skeleton;
```

## Style our Skeleton Card

You can view the whole files and directories [here](https://github.com/EJINEROZ/css-skeleton). We removed the styling from `index.css` to replace it with our own CSS style.

This typeface is nothing more than a grid arrangement. Our blog site should now display as follows when the Blog component is imported inside the App component:

![5](https://blog.openreplay.com/images/build-skeleton-screen-animations-with-css/images/image05.gif)



## Conclusion

We now know what skeleton loading is, how it benefits users by providing the impression that data is loading quickly, and how to use it. You can access the source code for the tutorial [here](https://github.com/EJINEROZ/css-skeleton). It is open to modification, customization, and code improvements.

## Resources
- [Github Repo](https://github.com/EJINEROZ/css-skeleton)


> A TIP FROM THE EDITOR: On the topic of measuring performance, do read our [Top Metrics You Need to Understand When Measuring Front-end Performance](https://blog.openreplay.com/top-metrics-you-need-to-understand-when-measuring-front-end-performance) and [A Practical Guide To Optimizing Performance On The Web](https://blog.openreplay.com/a-practical-guide-to-optimizing-performance-on-the-web/) articles.




[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)