Skip to content

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 ID
  • steps (array): Array of step objects
  • vertical (boolean): Vertical layout
  • size (‘sm’ | ‘md’ | ‘lg’): Step size
  • disabled (boolean): Disable all steps
  • clickable (boolean): Allow step clicking
  • showConnector (boolean): Show connecting lines
  • connectorType (‘solid’ | ‘dashed’): Connector style
  • activeColor (string): Active step color
  • inactiveColor (string): Inactive step color

Events

  • update:modelValue: Step changed
  • step-click: Step clicked
  • complete: All steps completed
  • change: Active step changed

Slots

  • step: Custom step content
  • connector: Custom connector
  • description: Step description
  • icon: Step icon/number

Usage Examples

  1. 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>
  1. 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>
  1. 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

  1. Visual Design:

    • Clear step indicators
    • Consistent spacing
    • Visual hierarchy
    • Progress indication
  2. User Experience:

    • Step validation
    • Clear navigation
    • Progress persistence
    • Error handling
  3. Accessibility:

    • ARIA labels
    • Keyboard navigation
    • Focus management
    • Screen reader support
  4. Implementation:

    • State management
    • Form integration
    • Navigation logic
    • Data persistence

Common Use Cases

  1. 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>
  1. 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>
  1. 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