4

I’m building an application using Nuxt 3, I want to add a page loader until the website loads.

gaurav kerkar
  • 87
  • 1
  • 1
  • 5

5 Answers5

8

According to this article. There are a simple but limited solution and a fully customized one.

Built in <NuxtLoadingIndicator>

<NuxtLoadingIndicator> includes a loading progress bar and can be used like this

<template>
  <NuxtLayout>
    <NuxtLoadingIndicator />
    <NuxtPage />
  <NuxtLayout>
</template>

But it has only one pre-defined UI and it can only customized with these few properties

  • Color: The color of the loading bar.
  • Height: Height of the loading bar, in pixels (default is 3).
  • Duration: Duration of the loading bar, in milliseconds (default is 2000).
  • Throttle: Throttle the appearing and hiding, in milliseconds (default is 200).

Customizable Loader

If you need your custom loader like full-screen spinner with the backdrop, you need the different approach. This approach allows you to create any loader and show it when needed.

<template>
  <div class="h-screen">
    <div v-if="loading" class="fixed left-0 top-0 h-0.5 w-full z-50 bg-green-500" />

    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </div>
</template>

<script setup lang="ts">
  const nuxtApp = useNuxtApp();
  const loading = ref(false);
  nuxtApp.hook("page:start", () => {
    loading.value = true;
  });
  nuxtApp.hook("page:finish", () => {
    loading.value = false;
  });
</script>

Nuxt3 provides application runtime hooks that have interceptors for page:start and page:finish events. To use them properly you need to use these hooks in this way (in the app.vue or your custom component):

Muhammad Mahmoud
  • 621
  • 1
  • 5
  • 21
1

"The <NuxtLoadingIndicator> component displays a progress bar on page navigation." – from the Nuxt3 Documentation for <NuxtLoadingIndicator>.

I think that is what's used to display the loading indicator in dev setup as well.

floriankapaun
  • 472
  • 1
  • 4
  • 19
  • 2
    Looking through the source code for `NuxtLoadingIndicator` it doesn't actually show the current loading progress of the page and is simply just a bar that will grow over a given time. It's a quick and easy solution but doesn't give any meaningful information to the user. – The Sloth Jan 04 '23 at 16:14
0

i had made a custom solution for Nuxt3, using pinia for state management and global middleware and this solution works same as Nextjs-ProgressBar

store/ui.js

import { defineStore } from "pinia";

const useUiStore = defineStore("uiStore", {
    state: () => {
        return {
            pageLoader: false,
        };
    },
    actions: {
        setPageLoader(value) {
            this.pageLoader = value

        },
    },
});

if (import.meta.hot) {
    import.meta.hot.accept(acceptHMRUpdate(useUiStore, import.meta.hot));
}

export default useUiStore;

middleware/ui.global.js

import useUiStore from "~~/stores/ui";

export default defineNuxtRouteMiddleware(async(to, from) => {
    useUiStore().setPageLoader(true)
})

component/LoaderIndicator.vue

<script setup>
import useUiStore from '~~/stores/ui';

const nuxtApp = useNuxtApp();

nuxtApp.hook("page:finish", () => {
    if(useUiStore().$state.pageLoader == true){
        useUiStore().setPageLoader(false)
    }
});

</script>
<script>
export default {
    watch: {
    showLoader(newVal, oldVal) {
      if (newVal == true) {
        this.$refs.progressBar.style.opacity = '1';
        this.$refs.progressBar.style.transitionDuration = '1000ms';
        this.$refs.progressBar.style.width = '40%';
      } else if (newVal == false) {
        this.$refs.progressBar.style.transitionDuration = '1000ms';
        this.$refs.progressBar.style.width = '100%';
        setTimeout(() => {
          this.$refs.progressBar.style.opacity = '0';
          this.$refs.progressBar.style.transitionDuration = '0ms';
          this.$refs.progressBar.style.width = '0%';
        }, 1000);
      }
    },
  },
  computed: {
    showLoader() {
      return useUiStore().$state.pageLoader;
    }
  },
};
</script>
<template>
    <div ref="progressBar" class="fixed left-0 top-0 z-50 transition-all bg-orange h-[3px] LoadingBar shadow-[0_0_10px] shadow-orange " :style="{ width :'0%' }"></div>
</template>

app.vue

<template>
    <NuxtLayout>
        <LoaderIndicator />
        <NuxtPage />
    </NuxtLayout>
</template>

please comment for optimisation

Thank You

Chirag Joshi
  • 409
  • 1
  • 4
  • 13
0

Instead of using <NuxtLoadingIndicator> I've used global middleware + page:finish hook like:

<template>
    <div v-show="show">
        Loading...
    </div>
</template>

<script setup>
    const nuxtApp = useNuxtApp();
    const show = ref(false);

    addRouteMiddleware('global-loader', () => {
        show.value = true
    }, {
        global: true
    })

    nuxtApp.hook('page:finish', () => { show.value = false; })
</script>

So on every route change, we show the loader and hide it when the page is displayed.

You can replace template with the one you want, some spinner or something.

Scofield
  • 4,195
  • 2
  • 27
  • 31
0

I think this also helpful but it involves javascript.

<div id="loading">Loading...</div>

~/plugins/loading.ts

export default defineNuxtPlugin((nuxtApp) => {
    nuxtApp.hook('page:finish', () => {
        const loadingElement = document.querySelector('#loading')
        if (loadingElement) {
            loadingElement.remove()
        }
    })
})

Once the hook triggers, it will remove the spinner in the document.

ReaganM
  • 1,290
  • 1
  • 11