Adding a Loading Icon in Nuxt

Posted on
tutorial formcake

Today I’m going to discuss implementing a loading icon like you see in a lot of SPAs within the context of a Nuxt project I’m working on - Form Cast, a simple form backend-as-a-service.

The Icon

My project at the moment is pretty spartanly designed, with a plain white background. For my loading icon, I wanted something with a flash of both activity and color. After surfing around, I decided on this (it’s technically a typing activity icon, but who cares).

Let’s go through the entirety of this (at the moment) dead simple page component. This is in our Nuxt app at /pages/forms/index.vue.

First in our <script> tag we’re going to import our Forms component, our listForms function, which is simply an axios function that hits our API and returns an array of form objects, and our mapState function for our vuex store, which will allow us to pull our userToken out of local storage and make an API call to an authentication-protected resource.

<script>
import Forms from '@components/Forms.vue'
import listForms from "@assetsPath/js/services/form/list.js"
import { mapState } from 'vuex';

Now into the next section, our actual component. We’ll use our default layout and register our Forms component, will registering two data keys - forms (our array of form data) and loading (our loading icon UI variable, which we’ll set initially to true since the page will initially be loading). We’ll also use the mapState function to pull out our userToken JWT.

export default {
    layout: 'default',

    components: {
        Forms,
    },

    data: function() {
        return {
            forms: null,
            loading: true
        }
    },

    computed: {
        ...mapState([
            "userToken"
        ])
    },

Now we can just call the listForms function, passing in our auth token, and once we get everything back, set loading to false. We’ll need to make the mounted() function async so that we can use await. Then we can end things with a closing </script> tag.

async mounted() {
    this.forms = await listForms(this.userToken);
    this.loading = false;
},
</script>

So now we have a loading state variable being set to true or false depending on our API call - we just need to build some display elements around it.

For this we can use Vue’s v-if, simply showing the loading icon if loading is true and wrapping our actual content in a v-else <div>.

<template>
    <section class="register-page container">
        <div class="loading" v-if="loading">
            <img class="loading-icon" src="/loading.gif">
        </div>
        <div v-else>
            <div class="section-title-pane">
                <h1 class="section-title">My Forms</h1>
            </div>
            <Forms v-bind:forms="forms" />
        </div>
    </section>
</template>

Putting it all together now. We’ll also add in a nifty flexbox trick we can use to both horizontally and vertically center the loading icon, which we’ll set to a width of 100px.

<script>
import Forms from '@components/Forms.vue'
import listForms from "@assetsPath/js/services/form/list.js"
import { mapState } from 'vuex';

export default {
    layout: 'default',

    components: {
        Forms,
    },

    data: function() {
        return {
            forms: null,
            loading: true
        }
    },

    computed: {
        ...mapState([
            "userToken"
        ])
    },

    async mounted() {
        this.forms = await listForms(this.userToken);
        this.loading = false;
    },
}
</script>
<template>
    <section class="register-page container">
        <div class="loading" v-if="loading">
            <img class="loading-icon" src="/loading.gif">
        </div>
        <div v-else>
            <div class="section-title-pane">
                <h1 class="section-title">My Forms</h1>
            </div>
            <Forms v-bind:forms="forms" />
        </div>
    </section>
</template>

<style lang="scss">
.loading {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height:500px;
}

.loading-icon {
    width: 100px;
}
</style>

And we’re done!

Subscribe for more updates

Want to hear more about Form Cast as it goes from glint-in-its-founders-eyes to a real-live business? Subscribe for notification updates below.