How to make a skeleton loader using Vue

Create a component

In my case I name it SkeletonLoader.vue, you can name it however you want. I will be adding HTML, Javascript, and CSS inside this component. First, this is a simple HTML structure using Bootstrap 4 to create our placeholder. In this case I start with a col-xl-4 which will give a 4 column div inside the 12 column bootstrap grid, I will add an addition two columns, but right now let’s focus on what is going on here. 

<template>
    <div class="container" v-show="loading">
        <div class="row">
            <div class="col-xl-4 pt-5">
                <div class="placeholder wave">
                    <div class="square"></div>
                    <div class="line"></div>
                    <div class="line"></div>
                </div>
            </div>
        </div>
    </div>
</template>

Inside col-xl-4, you will see the placeholder, this is the wrapper for skeleton loader, the square and lines are within the placeholder, this will make the structure and you want to make this as close as possible to the content that will be loaded. Next, I will give you examples with a square, rectangle, and circle. You can make as many shapes as you want. These are inside the two col-xl-4 which will give you a total of 1 row in the 12 column grid. As a result, you will see a row with three columns with our skeleton placeholders.

<div class="container" v-show="loading">
    <div class="row">
        <div class="col-xl-4 pt-5">
            <div class="placeholder wave">
                <div class="square"><br></div>
                <div class="line"><br></div>
                <div class="line"><br></div>
            </div>
        </div>
        <div class="col-xl-4 pt-5">
            <div class="placeholder wave">
                <div class="circle"><br></div>
                <div class="line"><br></div>
                <div class="line"><br></div>
            </div>
        </div>
        <div class="col-xl-4 pt-5">
            <div class="placeholder wave">
                <div class="rectangle"><br></div>
                <div class="line"><br></div>
                <div class="line"><br></div>
            </div>
        </div>
    </div>
</div>

You can add as many lines as you want and different shapes if that is the case. What you want here is the most accurate representation of what the loaded content will look like. Next we will go over the script. This will register our Vue component and give us the right state and methods to work with our component. 

Register the Component

Now, we are going to register the component with the name of Skeleton Loader, then we are going to return the data which is the state of the component, In this case, we return loading: true. If loading is true then it will show our elements if not then it will hide them using display:none; 

name: 'SkeletonLoader',
  data () {
    return {
        loading: true
    }
},

<div class="container" v-show="loading">

Next, we are going to register the method setLoadingState(), this will simulate our call to the server, this is where you are going to load data within your component, you can also, use the loading as a prop and then pass it from the parent which is where you are going to load the data.

In this case, this component will call the setLoadingState() from its mounted() hook, so once the component loads it will call our method, the method changes the loading state after 5 seconds to demonstrate that the elements will hide. I will give you both examples so that you can implement it how you want. 

export default {
        name: 'SkeletonLoader',
        data () {
            return {
                loading: true
            }
        },
        mounted(){
            setTimeout(() => {
                this.setLoadingState(false)
            }, 5000)
        },
        methods: {
            setLoadingState (value) {
                this.loading = value
            }
        }
    }
props: {
  loading: {
    type: Boolean,
    default: false,
    required: true
  }
}

Styles

Finally, we will go over the CSS, in this case, I am using SCSS to build it. As you can see I am building the basic shapes we are using .square, .circle, .line, and .rectangle, you can create as many as you want. Then I am adding the two animations which will make it feel like its loading. I am using pulse and wave animations.

For pulse animation: pulse 1s infinite ease-in-out; -webkit-animation: pulse 1s infinite ease-in-out; will attach run infinitely and then attach the pulse animation which is registered after. 

For Wave animation: wave 1s infinite linear forwards; -webkit-animation: wave 1s infinite linear forwards; background: #f6f7f8; background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%); background-size: 800px 104px;

Next I will register the keyframes, for it there is a start at 0, a middle at 50% and and end at 100% for pulse, this will change the opacity on the color to create the pulsing animation that runs from the .pulse selector. Wave only has a start at 0 and an end at 100% which moves the background position this based on the background size state about of 800px. 

<style scoped lang="scss">
    .placeholder {
        margin: 15px;
        padding: 10px;
        border-radius: 5px;

        &.pulse div {
            animation: pulse 1s infinite ease-in-out;
            -webkit-animation: pulse 1s infinite ease-in-out;
        }

        &.wave div {
            animation: wave 1s infinite linear forwards;
            -webkit-animation: wave 1s infinite linear forwards;
            background: #f6f7f8;
            background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%);
            background-size: 800px 104px;
        }

        div {
            background: #E8E8E8;
        }

        .square {
            float: left;
            width: 30px;
            height: 30px;
            margin: 0 0 10px;
        }

        .rectangle {
            float: left;
            width: 40px;
            height: 30px;
            margin: 0 0 10px;
        }

        .line {
            height: 12px;
            margin: 0 0 10px 50px;
            width: 70%;
        }

        .circle {
            float: left;
            width: 40px;
            height: 40px;
            margin: 0 15px 10px 0;
            border-radius: 40px;
        }
    }


    @keyframes pulse {
        0% {
            background-color: rgba(165, 165, 165, .1);
        }
        50% {
            background-color: rgba(165, 165, 165, .3);
        }
        100% {
            background-color: rgba(165, 165, 165, .1);
        }
    }

    @-webkit-keyframes pulse {
        0% {
            background-color: rgba(165, 165, 165, .1);
        }
        50% {
            background-color: rgba(165, 165, 165, .3);
        }
        100% {
            background-color: rgba(165, 165, 165, .1);
        }
    }

    @keyframes wave {
        0% {
            background-position: -468px 0
        }
        100% {
            background-position: 468px 0
        }
    }

    @-webkit-keyframes wave {
        0% {
            background-position: -468px 0
        }
        100% {
            background-position: 468px 0
        }
    }
</style>

Conclusion

A skeleton loader is a great UI tool for you to let your users know that something is loading. By implementing it, you are giving the perception that the application is faster than what it is. This will help your users be more engaged and improve your SEO. Your code will now look something like any of the two examples, first with everything inside one component and second with a property in case you have a parent component. 

Credentials