UIProgressSteps
UIProgressSteps
A versatile progress steps component that visualizes progress through a sequence of steps, with support for custom styling, navigation, and step content.
<template> <UIProgressSteps v-model="currentStep" :steps="steps" :vertical="false" :size="'md'" @step-click="handleStepClick" > <template #step="{ step, index }"> <div class="flex items-center space-x-2"> <div class="w-8 h-8 rounded-full flex items-center justify-center" :class="getStepClass(step, index)" > <CheckIcon v-if="isStepComplete(index)" class="w-5 h-5" /> <span v-else>{{ index + 1 }}</span> </div> <div> <div class="font-medium">{{ step.title }}</div> <div class="text-sm text-gray-500"> {{ step.description }} </div> </div> </div> </template> </UIProgressSteps></template>
<script setup lang="ts">interface Step { id: string title: string description?: string completed?: boolean disabled?: boolean}
const steps = ref<Step[]>([ { id: 'details', title: 'Basic Details', description: 'Enter basic information' }, { id: 'address', title: 'Address', description: 'Enter address details' }, { id: 'review', title: 'Review', description: 'Review and submit' }])
const currentStep = ref('details')
const isStepComplete = (index: number) => { return index < steps.value.findIndex(step => step.id === currentStep.value)}
const getStepClass = (step: Step, index: number) => { if (isStepComplete(index)) return 'bg-primary-600 text-white' if (step.id === currentStep.value) return 'bg-primary-100 text-primary-600' return 'bg-gray-100 text-gray-500'}
const handleStepClick = (stepId: string) => { // Handle step navigation console.log('Step clicked:', stepId)}</script>Props
modelValue(string): Current step IDsteps(array): Array of step objectsvertical(boolean): Vertical layoutsize(‘sm’ | ‘md’ | ‘lg’): Step sizedisabled(boolean): Disable all stepsclickable(boolean): Allow step clickingshowConnector(boolean): Show connecting linesconnectorType(‘solid’ | ‘dashed’): Connector styleactiveColor(string): Active step colorinactiveColor(string): Inactive step color
Events
update:modelValue: Step changedstep-click: Step clickedcomplete: All steps completedchange: Active step changed
Slots
step: Custom step contentconnector: Custom connectordescription: Step descriptionicon: Step icon/number
Usage Examples
- Basic Steps:
<template> <UIProgressSteps v-model="activeStep" :steps="steps" /></template>
<script setup>const activeStep = ref('step1')const steps = [ { id: 'step1', title: 'Step 1' }, { id: 'step2', title: 'Step 2' }, { id: 'step3', title: 'Step 3' }]</script>- Form Wizard:
<template> <div class="space-y-6"> <UIProgressSteps v-model="currentStep" :steps="formSteps" :clickable="false" > <template #step="{ step, index }"> <div class="flex items-center space-x-3"> <div class="w-10 h-10 rounded-full flex items-center justify-center text-lg" :class="getStepClass(index)" > <CheckIcon v-if="isStepComplete(index)" class="w-6 h-6" /> <span v-else>{{ index + 1 }}</span> </div>
<div> <div class="font-medium">{{ step.title }}</div> <div class="text-sm text-gray-500"> {{ step.description }} </div> </div> </div> </template> </UIProgressSteps>
<div class="p-6 border rounded-lg"> <component :is="currentComponent" v-model="formData[currentStep]" @valid="setStepValidity" @next="nextStep" @back="previousStep" /> </div>
<div class="flex justify-between"> <UIButton v-if="!isFirstStep" @click="previousStep" > Back </UIButton>
<UIButton v-if="!isLastStep" type="primary" :disabled="!isStepValid" @click="nextStep" > Continue </UIButton>
<UIButton v-else type="primary" :disabled="!isFormValid" @click="submitForm" > Submit </UIButton> </div> </div></template>- Vertical Steps with Custom Content:
<template> <div class="flex"> <UIProgressSteps v-model="activeSection" :steps="sections" :vertical="true" class="w-64" > <template #step="{ step }"> <div class="p-4 hover:bg-gray-50 cursor-pointer" :class="{ 'bg-primary-50': step.id === activeSection }" > <div class="font-medium"> {{ step.title }} </div> <div class="text-sm text-gray-500"> {{ step.subtitle }} </div> <div v-if="step.status" class="mt-2" > <UIBadge :variant="getStatusVariant(step)"> {{ step.status }} </UIBadge> </div> </div> </template> </UIProgressSteps>
<div class="flex-1 p-6"> <component :is="getCurrentComponent" v-bind="getCurrentProps" /> </div> </div></template>Best Practices
-
Visual Design:
- Clear step indicators
- Consistent spacing
- Visual hierarchy
- Progress indication
-
User Experience:
- Step validation
- Clear navigation
- Progress persistence
- Error handling
-
Accessibility:
- ARIA labels
- Keyboard navigation
- Focus management
- Screen reader support
-
Implementation:
- State management
- Form integration
- Navigation logic
- Data persistence
Common Use Cases
- Multi-step Forms:
<template> <UIProgressSteps v-model="formStep" :steps="formSteps" :clickable="canNavigateFreely" > <div class="mt-6"> <component :is="currentFormComponent" v-model="formData" @valid="handleStepValidation" /> </div> </UIProgressSteps></template>- Onboarding Flow:
<template> <UIProgressSteps v-model="onboardingStep" :steps="onboardingSteps" class="onboarding-steps" > <template #step="{ step }"> <div class="flex items-center space-x-3"> <component :is="step.icon" class="w-6 h-6" /> <span>{{ step.title }}</span> </div> </template> </UIProgressSteps></template>- Installation Progress:
<template> <UIProgressSteps v-model="installStep" :steps="installationSteps" :disabled="true" > <template #step="{ step }"> <div class="flex items-center justify-between"> <span>{{ step.title }}</span> <UISpinner v-if="step.loading" size="sm" /> </div> </template> </UIProgressSteps></template>Component Composition
The UIProgressSteps component works well with:
- UIButton for navigation
- UIForm for step content
- UISpinner for loading states
- UIBadge for status indicators
- UIIcon for step icons