Kamal Nasser

Preventing Loading Progress Bar Flashes with Vue.js

September 09 2018 post

The Problem

You're using a loading progress bar like the one shown below, but your website is fast enough for it to flash for a few milliseconds and disappear near instantly. This can make users feel that your website is slower than it actually is.

NProgress demo

The Solution

Display a progress bar only if the request takes "too long." A good duration to use 200ms.

The Code

const progressFns = () => {
    let progressTimeout, count = 0

    const start = () => {
        count++

        progressTimeout = setTimeout(() => {
            NProgress.start()
        }, 200)
    }

    const stop = () => {
        count = Math.max(0, count - 1)
        if (count > 0) return

        NProgress.done()
        clearTimeout(progressTimeout)
    }

    return { start, stop }
}

progressFns returns an object with two functions:

  • start to queue the progress bar for display after 200ms
  • stop to hide the progress bar

It also keeps track of how many requests are in progress and only hides the bar when the last one is done. Usually you would intercept your request library and/or Vue Router's requests and show/hide the progress bar as needed.

Usage

Get instances of the functions:

const {
    start: progressStart,
    stop: progressStop,
} = progressFns()

Vue.js

router.beforeResolve((to, from, next) => {
    if (to.name) {
        progressStart()
    }

    next()
})

router.afterEach((to, from) => {
    progressStop()
})

Axios

window.axios.interceptors.request.use(config => {
    if (!config.__noProgress) progressStart()

    return config
}, error => {
    progressStop()

    return Promise.reject(error)
})

window.axios.interceptors.response.use(response => {
    if (!response.config.__noProgress) progressStop()

    return response
}, error => {
    progressStop()

    return Promise.reject(error)
})

Checking config.__noProgress lets me selectively disable the progress bar completely for certain requests, like so:

axios
    .get("/auth/me", { __noProgress: true })
    ...

Thanks to Next.js 7 for making me aware of this!
https://github.com/jamiebuilds/react-loadable#avoiding-flash-of-loading-component