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:
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.
Let’s create a new Vue project. This tutorial will use Vue CLI (command line interface) to scaffold your application:
- 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:
- cd vue-loading-indicators
You can view your application in your web browser by running the following command:
- 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.
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:
- nano public/index.html
And add the snippet below before the closing </head>
tag:
<head>
<!-- ... -->
<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>
</head>
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.
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.”
Vue Router
comes with hooks you can hook into that lets you accomplish this functionality.
Open the src/router/index.js
file:
- 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.
NProgress.start()
}
next()
})
router.afterEach((to, from) => {
// Complete the animation of the route progress bar.
NProgress.done()
})
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:
- 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.
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.
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
:
- npm install axios@0.20.0
Then create an HTTP.js
file:
- 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 => {
NProgress.start()
return config
})
// before a response is returned stop nprogress
instance.interceptors.response.use(response => {
NProgress.done()
return response
})
export default instance
This code allows you to handle your connections and get a progress bar every time a request is made.
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
:
<template>
<DownloadButton :loading="loading">Download</DownloadButton>
</template>
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.
<template>
<button class="Button" :disabled="loading">
<svg v-if="loading" class="Button__Icon Button__Spinner" viewBox="...">
<path d="..."/>
</svg>
<svg v-else class="Button__Icon" viewBox="0 0 20 20">
<path d="..."/>
</svg>
<span v-if="!loading" class="Button__Content">
<slot/>
</span>
</button>
</template>
<script>
export default {
props: {
loading: { type: Boolean }
}
}
</script>
<style>
/* ... */
</style>
Here is a demonstration of what this code would produce:
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}">
<slot/>
</div>
`,
props: ['loading']
}
const Stats = {
template: `
<Loader :loading="updating">
...
</Loader>
`,
props: ['updating']
}
const app = new Vue({
template: `
<div class="widget">
<Stats :updating="fetchingStats"/>
<Button @click="fetchingStats = true">
Latest stats
</Button>
</div>
`,
})
Here is a demonstration of what this code would produce:
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.
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!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
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’;