How to setup global loading in Vue with Axios interceptors

Working on the client side there will always be involved in interacting with API as a bridge to insert and modify data to the database but if we don’t have proper loading on the client side when the client side consumes API it’s will many conflicts or request to the API leading to many mistakes. This article will show how to centralize loading whenever the front end requests the API via Axios interceptors (request/response).

Base loading component

Loading component show based on the isLoading state and the loading spinner style with raw CSS.

In the ./src/components/BaseLoading.vue

<template>
  <div v-if="isLoading" class="overlay">
    <div class="loader"></div>
  </div>
</template>

<script>
import { mapState } from "vuex";

export default {
  computed: {
    ...mapState("loading", ["isLoading"]),
  },
};
</script>

<style scoped>
.overlay {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 99999;
  cursor: pointer;
}
.loader {
  position: fixed;
  left: 50%;
  top: 50%;
  width: 60px;
  height: 60px;
  margin: -76px 0 0 -76px;
  border: 8px solid #f3f3f3;
  border-radius: 50%;
  border-top: 8px solid #3498db;
  -webkit-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
  0% {
    -webkit-transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
  }
}
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>

Loading store module

Set up a loading store module to control the loading state, when to show loading, and when to not. We use the Vuex state to control the loading state because it gives a reactivity so the component knows that the state is changed without refreshing the page. If you are not familiar with Vuex see more at Complete Guide to Vuex Store: Everything You Need to Know.

In the ./src/stores/modules/loading.js

export default {
  namespaced: true,

  state: () => ({
    isLoading: false,
  }),
  mutations: {
    setLoading(state, val) {
      state.isLoading = val;
    },
  },
};

Import loading module to base Vuex store.

In the ./src/stores/index.js

import Vue from "vue";
import Vuex from "vuex";

import loading from "./modules/loading";

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    loading,
  },
});

Axios interceptors

Set loading state true/false when API request via Axios. In this base Axios we use Axios interceptors request which will trigger when the Axios request happened to set loading to true, and Axios interceptors response which will trigger when the Axios response data sets loading to false.

In the ./src/axios.js

import axios from "axios";
import store from "./stores";

const http = axios.create({
  baseURL: "http://127.0.0.1:8000/api",
  headers: {
    Accept: "application/json",
  },
});

http.interceptors.request.use((config) => {
  store.commit("loading/setLoading", true);
  return config;
});

http.interceptors.response.use(
  (res) => {
    store.commit("loading/setLoading", false);
    return Promise.resolve(res.data);
  },
  (err) => {
    store.commit("loading/setLoading", false);
    return Promise.reject(err);
  }
);

export default http;

Integrate loading component

Add base loading component to main Vue application. This will have our loading component on every page as we added it in the main component application.

In the ./src/App.vue

<template>
  <div id="app">
    <router-view></router-view>
    <base-loading></base-loading>
  </div>
</template>

<script>
import BaseLoading from "./components/BaseLoading";

export default {
  components: {
    BaseLoading,
  },
  name: "App",
};
</script>

<style>
#app {
  padding: 30px;
}
</style>