Skip to content

UILoadingBar

UILoadingBar

A versatile loading bar component that provides visual feedback for page loads, form submissions, and other loading states with customizable styles and behaviors.

<template>
<UILoadingBar
:loading="loading"
:progress="progress"
:height="4"
color="primary"
/>
</template>
<script setup lang="ts">
const loading = ref(false)
const progress = ref(0)
const startLoading = () => {
loading.value = true
progress.value = 0
const interval = setInterval(() => {
if (progress.value < 90) {
progress.value += 10
} else {
clearInterval(interval)
finishLoading()
}
}, 200)
}
const finishLoading = () => {
progress.value = 100
setTimeout(() => {
loading.value = false
}, 200)
}
</script>

Props

  • loading (boolean): Show loading bar
  • progress (number): Progress percentage
  • height (number): Bar height in pixels
  • color (string): Bar color
  • duration (number): Animation duration
  • easing (string): Animation easing
  • fixed (boolean): Fixed positioning
  • zIndex (number): Z-index value
  • showSpinner (boolean): Show loading spinner
  • failedColor (string): Color for failed state
  • autoFinish (boolean): Auto finish on 100%

Events

  • start: Loading started
  • progress: Progress updated
  • finish: Loading finished
  • fail: Loading failed

Slots

  • spinner: Custom spinner content

Usage Examples

  1. Basic Loading Bar:
<template>
<UILoadingBar
:loading="loading"
:progress="progress"
/>
</template>
<script setup>
const loading = ref(false)
const progress = ref(0)
</script>
  1. Page Navigation Loading:
<template>
<div>
<UILoadingBar
:loading="navigating"
:progress="navigationProgress"
:height="2"
color="primary"
fixed
:zIndex="100"
/>
<nav class="flex space-x-4">
<RouterLink
v-for="route in routes"
:key="route.path"
:to="route.path"
class="px-4 py-2"
>
{{ route.name }}
</RouterLink>
</nav>
<RouterView />
</div>
</template>
<script setup>
const router = useRouter()
const navigating = ref(false)
const navigationProgress = ref(0)
router.beforeEach(() => {
navigating.value = true
navigationProgress.value = 0
const interval = setInterval(() => {
if (navigationProgress.value < 90) {
navigationProgress.value += 10
} else {
clearInterval(interval)
}
}, 100)
})
router.afterEach(() => {
navigationProgress.value = 100
setTimeout(() => {
navigating.value = false
}, 200)
})
</script>
  1. Form Submission Progress:
<template>
<form @submit.prevent="handleSubmit">
<UILoadingBar
:loading="submitting"
:progress="uploadProgress"
color="success"
:height="4"
>
<template #spinner>
<div class="flex items-center space-x-2">
<UISpinner size="sm" />
<span class="text-sm">
{{ uploadProgress }}%
</span>
</div>
</template>
</UILoadingBar>
<div class="space-y-4">
<div class="space-y-2">
<label class="text-sm font-medium">
File
</label>
<input
type="file"
@change="handleFileChange"
/>
</div>
<div class="flex justify-end">
<UIButton
type="primary"
:loading="submitting"
:disabled="!selectedFile"
>
Upload
</UIButton>
</div>
</div>
</form>
</template>
<script setup>
const submitting = ref(false)
const uploadProgress = ref(0)
const selectedFile = ref(null)
const handleFileChange = (event) => {
selectedFile.value = event.target.files[0]
}
const handleSubmit = async () => {
if (!selectedFile.value) return
submitting.value = true
uploadProgress.value = 0
try {
await uploadFile(selectedFile.value, (progress) => {
uploadProgress.value = Math.round(progress)
})
} catch (error) {
// Handle error
} finally {
submitting.value = false
}
}
</script>

Best Practices

  1. User Experience:

    • Smooth animations
    • Clear progress
    • Visual feedback
    • Error states
  2. Performance:

    • Efficient updates
    • Minimal reflows
    • Memory management
    • Animation optimization
  3. Accessibility:

    • ARIA labels
    • Progress announcements
    • Screen reader support
    • Focus management
  4. Mobile:

    • Touch feedback
    • Responsive design
    • Performance optimization
    • Clear indicators

Common Use Cases

  1. API Request Loading:
<template>
<div class="space-y-4">
<UILoadingBar
:loading="loading"
:progress="requestProgress"
color="primary"
/>
<div class="flex items-center justify-between">
<h2 class="text-lg font-medium">
Data
</h2>
<UIButton
:loading="loading"
@click="fetchData"
>
Refresh
</UIButton>
</div>
<UITable
:data="tableData"
:columns="columns"
:loading="loading"
/>
</div>
</template>
<script setup>
const loading = ref(false)
const requestProgress = ref(0)
const fetchData = async () => {
loading.value = true
requestProgress.value = 0
try {
const interval = setInterval(() => {
if (requestProgress.value < 90) {
requestProgress.value += 10
}
}, 100)
const data = await fetchTableData()
tableData.value = data
clearInterval(interval)
requestProgress.value = 100
} finally {
setTimeout(() => {
loading.value = false
}, 200)
}
}
</script>
  1. Multi-step Process:
<template>
<div class="space-y-6">
<UILoadingBar
:loading="processing"
:progress="totalProgress"
color="primary"
:height="8"
>
<template #spinner>
<div class="text-sm font-medium">
{{ currentStep }}/{{ totalSteps }}
</div>
</template>
</UILoadingBar>
<div class="space-y-4">
<div class="text-lg font-medium">
{{ stepTitle }}
</div>
<p class="text-gray-600">
{{ stepDescription }}
</p>
<div class="flex justify-between">
<UIButton
variant="outline"
:disabled="processing || currentStep === 1"
@click="previousStep"
>
Previous
</UIButton>
<UIButton
type="primary"
:loading="processing"
@click="nextStep"
>
{{ isLastStep ? 'Finish' : 'Next' }}
</UIButton>
</div>
</div>
</div>
</template>
<script setup>
const processing = ref(false)
const currentStep = ref(1)
const totalSteps = 4
const totalProgress = computed(() => {
return (currentStep.value - 1) * (100 / totalSteps)
})
const nextStep = async () => {
processing.value = true
try {
await processStep(currentStep.value)
if (currentStep.value < totalSteps) {
currentStep.value++
}
} finally {
processing.value = false
}
}
</script>
  1. File Download:
<template>
<div class="space-y-2">
<UILoadingBar
:loading="downloading"
:progress="downloadProgress"
color="success"
/>
<div class="flex items-center justify-between">
<div>
<div class="font-medium">
{{ fileName }}
</div>
<div class="text-sm text-gray-500">
{{ formatFileSize(fileSize) }}
</div>
</div>
<UIButton
:loading="downloading"
@click="downloadFile"
>
<DownloadIcon
class="w-5 h-5 mr-2"
/>
Download
</UIButton>
</div>
</div>
</template>
<script setup>
const downloading = ref(false)
const downloadProgress = ref(0)
const downloadFile = async () => {
downloading.value = true
downloadProgress.value = 0
try {
await download(fileUrl, (progress) => {
downloadProgress.value = progress
})
} finally {
downloading.value = false
}
}
</script>

Component Composition

The UILoadingBar component works well with:

  • UISpinner for loading indicators
  • UIButton for actions
  • UIProgress for detailed progress
  • UITooltip for progress details
  • UIIcon for status indicators
  • UINotification for completion
  • UICard for loading containers
  • UITable for data loading