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 barprogress(number): Progress percentageheight(number): Bar height in pixelscolor(string): Bar colorduration(number): Animation durationeasing(string): Animation easingfixed(boolean): Fixed positioningzIndex(number): Z-index valueshowSpinner(boolean): Show loading spinnerfailedColor(string): Color for failed stateautoFinish(boolean): Auto finish on 100%
Events
start: Loading startedprogress: Progress updatedfinish: Loading finishedfail: Loading failed
Slots
spinner: Custom spinner content
Usage Examples
- Basic Loading Bar:
<template> <UILoadingBar :loading="loading" :progress="progress" /></template>
<script setup>const loading = ref(false)const progress = ref(0)</script>- 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>- 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
-
User Experience:
- Smooth animations
- Clear progress
- Visual feedback
- Error states
-
Performance:
- Efficient updates
- Minimal reflows
- Memory management
- Animation optimization
-
Accessibility:
- ARIA labels
- Progress announcements
- Screen reader support
- Focus management
-
Mobile:
- Touch feedback
- Responsive design
- Performance optimization
- Clear indicators
Common Use Cases
- 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>- 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 = 4const 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>- 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