How To Add Loading Indicators to a Vue.js Application

Updated on September 23, 2020

Samuel Oloruntoba and Bradley Kouchi

How To Add Loading Indicators to a Vue.js Application


Loading indicators improve UX (user experience) in any application—web or mobile. These animations provide feedback to the user that an action is being carried out and a result will return shortly.

In web applications, there are two major events that need loaders:

  • Network requests like navigating to a different page or an AJAX request.
  • When a heavy computation is running.

This article will introduce a few ways you can add loaders to your Vue.js applications.


To complete this tutorial, you will need:

This tutorial was verified with Node v14.2.0, npm v6.14.5, vue v2.6.11, and vue-router v3.1.6.

Step 1 — Setting up the Project

Let’s create a new Vue project. This tutorial will use Vue CLI (command line interface) to scaffold your application:

  1. npx @vue/cli@4.5.4 create --inlinePreset='{ "useConfigFiles": false, "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "base", "lintOn": ["save"] } }, "vuex": true, "router": true, "routerHistoryMode": true }' vue-loading-indicators

This long command is a set of presets based on defaults established by @vue/cli/packages/@vue/cli/lib/options.js. When reformatted for readability, it looks look like this:

  "useConfigFiles": false,
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "base",
      "lintOn": ["save"]
  "vuex": true,
  "router": true,
  "routerHistoryMode": true

These presets add vue-router as a plugin (cli-plugin-router), add vuex as a plugin (cli-plugin-vuex), enable history mode, add Babel, and add ESLint.

For the needs of this tutorial, you will not require TypesScript, Progressive Web App (PWA) support, Vuex, CSS pre-processors, unit testing, or end-to-end (E2E) testing.

Next, change into the new project directory:

  1. cd vue-loading-indicators

You can view your application in your web browser by running the following command:

  1. npm run serve

When you visit localhost:8080 in a web browser, you should see a "Welcome to Your Vue.js App" message. There will also be links to a Home page and About page since you included Vue Router. Your goal will be to add a loading indicator when naviagting between these two pages.

If you want to learn more about Vue CLI, you can refer to the Vue CLI documentation.

Now, you are ready to start building.

Step 2 — Using nprogress

You can use any loading indicator of your choice. For this tutorial, you will install nprogress to use as your loading indicator.

You won’t be using npm or yarn for this. You will reference it from a CDN (content delivery network).

In the created Vue CLI project, navigate to public/index.html file:

  1. nano public/index.html

And add the snippet below before the closing </head> tag:

  <!-- ... -->
  <link href="https://unpkg.com/nprogress@0.2.0/nprogress.css" rel="stylesheet" />
  <script src="https://unpkg.com/nprogress@0.2.0/nprogress.js"></script>

nprogress exposes a few API methods, but for this article, you will require two—start and done. These methods start and stop the progress bar.

nprogress can also be configured for how to progress the loader. Although this can be manually customized, this article will use the default behavior.

Step 3 — Using the Router

When you use the router to add a progress bar to your website, the typical functionality you expect would be:

“When a user navigates to a new page, the loader starts ticking at the top of the page showing the user the download progress of the next page.”

Animated gif depicting a loading progress bar across the top of the screen.

Vue Router comes with hooks you can hook into that lets you accomplish this functionality.

Open the src/router/index.js file:

  1. nano src/router/index.js

And add the code below before the export:

// ...

router.beforeResolve((to, from, next) => {
  // If this isn't an initial page load.
  if (to.name) {
    // Start the route progress bar.

router.afterEach((to, from) => {
  // Complete the animation of the route progress bar.

export default router

When you hook to the beforeResolve route, you are telling the router to start nprogress once a page request happens. The afterEach hook tells the router that after a link has completely evaluated to stop the progress bar, it shouldn’t care if the page request succeeds.

At this point, you can run your application:

  1. npm run serve

When you visit localhost:8080 in your web browser, you can navigate between the Home page and the About page. As you do so, you will see the loading indicator that you added to your application.

The next step will further explore loading indicators.

Step 4 — Exploring Advanced Usage

In this section, you will be introduced to other use cases for loading indicators in your application.

These will be presented with examples and you will not be directly implementing them in your tutorial unless you feel like exploring on your own.

Using a HTTP library

Another part of the application for which you may like to add progress bars is when your user makes an AJAX request.

Most HTTP libraries today have a sort of middleware or interceptor that fires before a request or response happens. Because of this, you can also hook into your library of choice.

For this tutorial, you will be using axios. This library uses the term interceptors.

Install axios:

  1. npm install axios@0.20.0

Then create an HTTP.js file:

  1. nano HTTP.js

Then, you can configure axios to work like this:

import axios from 'axios'

// create a new axios instance
const instance = axios.create({
  baseURL: '/api'

// before a request is made start the nprogress
instance.interceptors.request.use(config => {
  return config

// before a response is returned stop nprogress
instance.interceptors.response.use(response => {
  return response

export default instance

This code allows you to handle your connections and get a progress bar every time a request is made.

Using Loaders within Components

There are times when you are not making a page request or an AJAX request. It may just be a browser dependent action that takes time.

Let’s consider a custom DownloadButton component that can change its state to a loading state due to some external action.

The component will take only one prop, loading:

  <DownloadButton :loading="loading">Download</DownloadButton>

The example component would resemble something like this:

Note: Some of the finer details of SVG viewBox, path, and style are not defined in this example.

  <button class="Button" :disabled="loading">
    <svg v-if="loading" class="Button__Icon Button__Spinner" viewBox="...">
      <path d="..."/>
    <svg v-else class="Button__Icon" viewBox="0 0 20 20">
      <path d="..."/>
    <span v-if="!loading" class="Button__Content">

export default {
  props: {
    loading: { type: Boolean }

  /* ... */

Here is a demonstration of what this code would produce:

Animated gif of user interacting with a download button and a loading circle appearing.

Using Higher-Order Components for Reusable Loaders

You can create loaders as wrappers (HOCs) for our components that you can then modify their state via props.

These type of loaders are good for components that don’t affect the global state of your application, but you still want the user to feel connected to the action in place.

Here is an example:

Note: Some of the finer details of the contents of Stats are not defined in this example.

// This loader will add an overlay with the text of 'Loading...'
const Loader = {
  template: `
    <div class="{'is-loading': loading}">
  props: ['loading']

const Stats = {
  template: `
    <Loader :loading="updating">
  props: ['updating']

const app = new Vue({
  template: `
    <div class="widget">
      <Stats :updating="fetchingStats"/>
      <Button @click="fetchingStats = true">
        Latest stats

Here is a demonstration of what this code would produce:

Box with an update button, the user clicks update, and the box grayed out to display a loading message

Using Asynchronous Vue Components

Vue’s asynchronous components allows you to fetch components from the server only when you need them. Instead of serving end users components they might never use, end users are served only what they need.

Async components also come with native support for loading and error states, so there is no need for any additional configuration here.

Here is an example:

const AsyncComponent = () => ({
  component: import('./MyComponent.vue'),
  // show this component during load
  loading: LoadingComponent,
  // show this component if it fails to load
  error: ErrorComponent,
  // delay before showing the loading component
  delay: 200,
  // error if the component failed to loadin is allotted time in milliseconds default in Infinity
  timeout: 3000

To use async components with the method here, you need to use Vue Router lazy loading.


In this article, you explored some of the ways to add a loading indicator to your Vue.js app. Loading indicators are a useful tool to provide feedback to users.

If you’d like to learn more about Vue.js, check out our Vue.js topic page for exercises and programming projects.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us

About the authors
Default avatar
Samuel Oloruntoba


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

The author does not mention how to import NProgress when installed using npm. You should add the following at the top of each file you use NProgress:

import NProgress from ‘nprogress’ import ‘nprogress/nprogress.css’;

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel