A lightweight, beautiful, and customizable global loading overlay component for Vue 3 and Nuxt 3. Includes TypeScript support and multiple ways to control the loading state.
✨ Global Loading Overlay - Fullscreen, centered spinner overlay 🎨 Beautiful Animations - Smooth fade transitions with animated dots 📱 Fully Responsive - Works on all screen sizes 🔧 Easy to Use - Simple API with multiple control methods 📘 TypeScript Support - Full type safety and IDE autocompletion 🚀 Nuxt 3 Compatible - Works seamlessly with Nuxt 3 ♿ Accessible - ARIA labels and semantic HTML 💨 Lightweight - Minimal bundle size
npm install @jalzae/vue-loadingyarn add @jalzae/vue-loadingpnpm add @jalzae/vue-loading// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import JalzLoading from '@jalzae/vue-loading'
const app = createApp(App)
// Install the plugin
app.use(JalzLoading)
app.mount('#app')<template>
<button @click="showLoading">Show Loading</button>
</template>
<script setup lang="ts">
import { setLoading, unsetLoading } from '@jalzae/vue-loading'
const showLoading = () => {
setLoading()
// do something
setTimeout(() => unsetLoading(), 3000)
}
</script>// plugins/jalz-loading.ts
import JalzLoading from '@jalzae/vue-loading'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(JalzLoading)
})// composables/useApi.ts
import { setLoading, unsetLoading } from '@jalzae/vue-loading'
export const useApi = () => {
const fetchData = async () => {
setLoading()
try {
// your API call
const data = await $fetch('/api/data')
return data
} finally {
unsetLoading()
}
}
return { fetchData }
}import { setLoading, unsetLoading } from '@jalzae/vue-loading'
// Show loading
setLoading()
// Hide loading
unsetLoading()
// Or with async operations
const loadData = async () => {
setLoading()
try {
const data = await fetchData()
return data
} finally {
unsetLoading()
}
}// In Options API or any component
export default {
methods: {
async fetchData() {
this.$setLoading()
try {
const data = await this.api.getData()
this.data = data
} finally {
this.$unsetLoading()
}
}
}
}// In any JavaScript code, outside Vue
window.setLoading()
// do something
window.unsetLoading()Shows the loading overlay. Optionally set a specific value.
import { setLoading } from '@jalzae/vue-loading'
setLoading() // Show loading
setLoading(true) // Show loading (explicit)Hides the loading overlay.
import { unsetLoading } from '@jalzae/vue-loading'
unsetLoading()Reactive ref for the loading state. Use if you need to observe the loading state.
import { loadingVisible } from '@jalzae/vue-loading'
import { watch } from 'vue'
watch(loadingVisible, (isLoading) => {
console.log('Loading:', isLoading)
})When the plugin is installed, these are available via app.config.globalProperties:
$setLoading- Show loading overlay$unsetLoading- Hide loading overlay
<template>
<div>
<button @click="fetchUsers">Load Users</button>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { setLoading, unsetLoading } from '@jalzae/vue-loading'
const users = ref([])
const fetchUsers = async () => {
setLoading()
try {
const response = await fetch('/api/users')
users.value = await response.json()
} catch (error) {
console.error('Failed to fetch users:', error)
} finally {
unsetLoading()
}
}
</script><template>
<form @submit.prevent="submitForm">
<input v-model="name" type="text" placeholder="Name" />
<input v-model="email" type="email" placeholder="Email" />
<button type="submit">Submit</button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { setLoading, unsetLoading } from '@jalzae/vue-loading'
const name = ref('')
const email = ref('')
const submitForm = async () => {
setLoading()
try {
await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: name.value, email: email.value })
})
alert('Form submitted!')
} finally {
unsetLoading()
}
}
</script>const runLongOperation = async () => {
setLoading()
// Simulate 5 seconds of work
await new Promise(resolve => setTimeout(resolve, 5000))
unsetLoading()
console.log('Operation complete!')
}The loading overlay has the following CSS structure:
/* Fullscreen overlay */
.vue-loading-overlay {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, 0.7);
z-index: 9999;
}
/* Spinner container */
.vue-loading {
width: 80px;
height: 80px;
}
/* Individual dots */
.vue-loading__dot {
background-color: #ad5160;
animation: vue-loading-fade 2s linear infinite;
}To customize colors, you can override these styles in your global CSS:
/* your-global-styles.css */
.vue-loading__dot {
background-color: #your-color !important;
}
.vue-loading-overlay {
background: rgba(0, 0, 0, 0.5) !important;
}Full TypeScript support with proper type definitions:
import { setLoading, unsetLoading, loadingVisible } from '@jalzae/vue-loading'
import type { Ref } from 'vue'
// All functions are properly typed
setLoading() // void
unsetLoading() // void
const visible: Ref<boolean> = loadingVisible- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Bundle Size: ~2KB minified + gzipped
- Runtime: No performance impact, uses Vue's efficient reactivity system
- Memory: Minimal memory footprint
MIT License - feel free to use in your projects!
Contributions are welcome! Please feel free to submit a Pull Request.
For issues, questions, or suggestions, please open an issue on GitHub.
Made with ❤️ by Jalzae