Creating Skeleton Loading Animation in React

Creating Skeleton Loading Animation in React

·

6 min read

Introduction

Loading screens are an important aspect for any application. This lets the user know that the processing is going on. There are lots of evolution in the loading screen. We have moved from the simple Loading…. message to the more advanced Skeleton Loading Animation.

Skeleton Loading animation is a more advanced loading animation and its looks dope. You have seen it before on YouTube and other platforms.

Today, we are going to create a Skeleton Loading Animation in React with the react-loading-skeleton library. It is one of the most used libraries for creating skeleton loading animation with a single component.

In the end, we will be able to achieve the below output for our loading screen.

LoadingSkeleton

So, let’s get started.

Setting Up the Environment

For creating React app, we have used Vite instead of CRA. Vite creates a react application with minimal already created files and code. This is quite fast in comparison with CRA. You can start by running the following command in the terminal:

    npm create vite@latest

After this, entered the name of the project in the terminal. I have used the skeleton-loading-react as the name. After hitting enter, choose the framework as React. Lastly, choose the programming language for the framework between JavaScript and TypeScript. This will create the react application instantly. Change the directory to skeleton-loading-react and run npm install in the terminal to install the packages. Additionally, we need to install the skeleton loading animation. Run the below command in the terminal:

    npm i react-loading-skeleton

Other dependencies I have used

  • Axios: For making calls to the API
  • Chakra UI: For building the component

What are we building?

We are going to use the MovieDB API to fetch the trending movies and display them in the card component. While making the call, we are going to display our Loading Skeleton.

App.js

Let’s build the project with one component at a time. Starting with the first component App.js. Let’s look into the code then I explain it.

    import { useState, useEffect } from 'react'
    import './App.css'
    import  axis from 'axios'
    import AnimeCard from './components/AnimeCard'
    import {Grid} from '@chakra-ui/react'
    import LoadingSkeleton from './components/LoadingSkeleton'

    function App() {
      const [loading, setLoading] = useState(true) //state for loading
      const [data, setData] = useState() // data is extracted from the API
      const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // array for number of loading skeleton
      useEffect(() => {
        let timerId;
        if (loading) {
          timerId = setTimeout(async () => {
            await axios.get(`https://api.themoviedb.org/3/trending/movie/week?api_key=${import.meta.env.VITE_MOVIEDB_KEY}`)
            .then(res => {
              setData(res.data.results)
            })
            setLoading(false)
          }, 2000);
        }
        return () => clearTimeout(timerId);
      }, [loading])
      return ( 
        <div className="App">
          <h1>Trending Movie List</h1>
          <div className='container'>
            {loading && 
              <Grid templateColumns='repeat(4, 1fr)' gap={6}>
                {
                  arr.map(item => {
                    return  <LoadingSkeleton/>
                  })
                }
            </Grid>  
            }
            {
              !loading && 
              <Grid templateColumns='repeat(4, 1fr)' gap={6}>
                {data && data.map((item, index) => {
                  return <AnimeCard name={item.original_title} img={item.poster_path} genre_id={item.genre_ids[0]} />
                })}
              </Grid>        
            }
          </div>
        </div>
      )
    }
    export default App

At the top, we are importing the necessary modules and libraries. In the App function, we use useState to define the variable. The comments explain their purposes. We have used the useEffect for making a call to MovieDB’s API using Axios. The function containing code for making a call only runs when the loading is set to true which is the default value. We have also used the setTimeout function to delay the calling so that we can see the loading animation. After making the call, we are storing the response in the data variable.

In return, we are conditionally rendering the component based on loading. If it is true we are loading the LoadingSkeleton component otherwise MovieCard.

MovieCard Component

Creating the final component should be done before creating the loading skeleton. This will give us insight into the loading component. We are creating the card in which the data of the movie will be displayed. We have named the component MovieCard.

Here is the code for the component:

    import React, {useState, useEffect} from 'react'
    import axios from 'axios'
    import {Image, Tag, Heading} from '@chakra-ui/react'
    const MovieCard = ({name, img, genre_id}) => {
      const [genre, setGenre] = useState()
      useEffect(() => {
        axios.get("https://api.themoviedb.org/3/genre/movie/list?api_key=1428e09fe38be9df57355a8d6198d916")
        .then(res => {
          let genre_name = res.data.genres.filter(item => item.id === genre_id)
          setGenre(genre_name[0].name)
        },[genre_id])
      })
      return (
        <div className='cardContainer'>
          <div className='cardImg'>
            <Image src={`https://image.tmdb.org/t/p/w500${img}`} />
          </div>
          <div className='tagContainer'>
            <Tag size="md" variant='solid' colorScheme='pink'>
              {genre}
            </Tag>
          </div>
          <div className='tagContainer'>
            <Heading color="white" size="md">{name}</Heading>
          </div>
        </div>
      )
    }
    export default MovieCard

In MovieDB API, the movie’s genre comes with a code and for accessing the name of the genre using the code, we are calling the API in the useEffect. The return statement is where we are defining the card for the movie. There is a parent container with the name cardContainer. Within1 this we have the Image, Genre name as a tag, and title of the movie.

You can find the CSS of the component here:

    .container {
      display: flex;
      justify-content: space-between;
      width: 100%;
      max-width: 1200px;
      margin: 1em auto;
    }
    .cardContainer {
      display: flex;
      flex-direction: column;
      cursor: pointer;
      background-color: #272D37;
      border-radius: 10px;
      margin-top: 2em;
      border: 1.5px solid transparent;
    }
    .cardImg{
      margin: 1em;
      border-radius: 1em;
    }
    .tagContainer {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      color: white;
      padding: 0 1em;
      padding-bottom: 1em;
    }

This will result into the card look like this:

MovieCard

LoadingSkeleton

Now it’s time to create the loading skeleton. The loading skeletons have the structure without the data. We have to create these structures as per our data. We are having an image container, a tag for the genre, and a heading tag with the title. Let’s discuss each component in our card one by one.

Parent Component

The parent component remains the same as it holds our component. We do not need to recreate this.

Image Container

We need to create a component with the same resolution as the image. This component will be displayed in place of an actual image while loading. Here is the CSS for my card image:

    .cardImgLoading{
      margin: 1em;
      width: 247px;
      height: 374px;
      background-color: #4a5568;
    }

Genre Tag

I used another container for displaying the genre. Here is the code for CSS:

    .tagContainerLoading{
      margin-left: -12em;
      margin-bottom: 1em;
      width: 50px;
      height: 20px;
      background-color: #D53F8C;
      border-radius: 10px;
    }

Title Container

The title is converted into a block component having a height and width. This will be useful in displaying the loading animation. Here is the code:

    .titleContainerLoading{
      margin-left: -3em;
      margin-bottom: 1em;
      width: 200px;
      height: 20px;
      background-color: #4a5568;
      border-radius: 10px;
    }

Skeleton Component

We now just need to pass the above CSS as the class name in the Skeleton component.

    <Skeleton highlightColor='#97a2b5' baseColor='#97a2b5' className='cardImgLoading'/>

Here is the description of the used props from loading-skeleton-react.

PropDescriptionDefault
baseColor?: stringThe background color of the skeleton.#ebebeb
highlightColor?: stringThe highlight color in the skeleton animation.#f5f5f5

The overall code of the LoadingSkeleton is:

    import React from 'react'
    import Skeleton from 'react-loading-skeleton' // importing the skeleton component
    import 'react-loading-skeleton/dist/skeleton.css' // importing the css for the animation
    const LoadingSkeleton = () => {
      return (
        <div className='cardContainer'>
          <Skeleton highlightColor='#97a2b5' baseColor='#97a2b5' className='cardImgLoading'/>
          <Skeleton highlightColor='#97a2b5' baseColor='#97a2b5' className='tagContainerLoading'/>
          <Skeleton highlightColor='#97a2b5' baseColor='#97a2b5' className='titleContainerLoading'/>
        </div>
      )
    }
    export default LoadingSkeleton

This will result in the following loading animation:

LoadingSkeleton

Live Working

I have created this project in a codesandbox container. You can see it here

Conclusion

We have successfully created a skeleton loading in React with skeleton-loading-react libraries. You can explore with the Sandbox and create your own loading skeleton.

I hope, this article has helped in understanding the loading skeleton in your React project. Thanks for reading the article.