UIPackage
Menu

Framework

Change language

Boilerplate repo

Stepper

stepperui

Multi-step indicator — horizontal or vertical, with completed / current / upcoming states and optional descriptions per step. Use for onboarding wizards and checkout flows.

Also available for React ->

When to use which

Decision Guide

Timeline showcases chronological events and milestones past-to-present. Stepper guides users through a linear, multi-step workflow requiring active user progression.

ComponentRole
Timeline

Chronological feeds, delivery tracking logs, audit trails, and product roadmap milestones.

Activity & Milestone Stream
Steppercurrent

Multi-step wizards, checkout sequences, onboarding flows, and form completion tracking.

Workflow Progression
  • Activity logs, tracking events, & milestones → Timeline (view)
  • Multi-step wizard or form progression → Stepper

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/stepper.json
Named registry:npx shadcn-vue@latest add @uipkge/stepperInstalls to:app/components/ui/stepper/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
status
'pending''active''completed''error'
pendingoptional
stepsStepperStep[]() => [], modelValue: 1, orientation: 'horizontal', size:…optional
modelValuenumberoptional
orientationStepperOrientationoptional
size
'sm''default''lg'
defaultoptional
classHTMLAttributes['class']optional

Schema

Type aliases from this item's source — use them to shape the data you pass in.

StepperContext
interface StepperContext {
  orientation: Ref<StepperOrientation>
  size: Ref<StepperSize>
  activeStep: Ref<number>
  steps: Ref<StepperStep[]>
  goToStep: (stepIndex: number) => void
  isClickable: (index: number) => boolean
  getStatus: (index: number) => StepperStatus
}
StepperStep
interface StepperStep {
  id: string | number
  title: string
  description?: string
  icon?: Component
  disabled?: boolean
  error?: boolean
}

Used by

Files installed (12)

  • app/components/ui/stepper/Stepper.vue2.1 kB
    <script setup lang="ts">
    import { computed, provide, toRef } from 'vue'
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    import { STEPPER_CONTEXT, type StepperOrientation, type StepperSize, type StepperStatus } from './context'
    import type { StepperStep } from './types'
    
    interface Props {
      steps?: StepperStep[]
      modelValue?: number
      orientation?: StepperOrientation
      size?: StepperSize
      class?: HTMLAttributes['class']
    }
    
    const props = withDefaults(defineProps<Props>(), {
      steps: () => [],
      modelValue: 1,
      orientation: 'horizontal',
      size: 'default',
    })
    
    const emit = defineEmits<{
      'update:modelValue': [value: number]
    }>()
    
    const activeStep = computed(() => props.modelValue)
    const stepsRef = computed(() => props.steps)
    
    function getStatus(index: number): StepperStatus {
      const step = props.steps[index]
      if (step?.error) return 'error'
      if (index + 1 === activeStep.value) return 'active'
      if (index + 1 < activeStep.value) return 'completed'
      return 'pending'
    }
    
    function isClickable(index: number): boolean {
      return index + 1 < activeStep.value
    }
    
    function goToStep(stepIndex: number) {
      if (stepIndex < 1 || stepIndex > props.steps.length) return
      const step = props.steps[stepIndex - 1]
      if (step?.disabled) return
      emit('update:modelValue', stepIndex)
    }
    
    provide(STEPPER_CONTEXT, {
      orientation: toRef(props, 'orientation'),
      size: toRef(props, 'size'),
      activeStep,
      steps: stepsRef,
      goToStep,
      isClickable,
      getStatus,
    })
    
    defineExpose({ goToStep })
    </script>
    
    <template>
      <div
        :class="cn('w-full', props.class)"
        role="tablist"
        :aria-orientation="orientation"
        :data-orientation="orientation"
      >
        <!-- Header strip with steps -->
        <slot name="steps">
          <ol
            v-if="steps.length > 0"
            :class="cn('flex', orientation === 'horizontal' ? 'flex-row items-start' : 'flex-col items-stretch')"
          >
            <StepperItem v-for="(step, index) in steps" :key="step.id" :step="step" :index="index" />
          </ol>
        </slot>
    
        <!-- Content area -->
        <div v-if="$slots.default" class="mt-6 flex-1">
          <slot :active-step="activeStep" :steps="steps" />
        </div>
      </div>
    </template>
    
  • app/components/ui/stepper/StepperItem.vue6.3 kB
  • app/components/ui/stepper/StepperIndicator.vue3.6 kB
  • app/components/ui/stepper/StepperHeader.vue0.6 kB
  • app/components/ui/stepper/StepperContent.vue1 kB
  • app/components/ui/stepper/StepperTitle.vue0.4 kB
  • app/components/ui/stepper/StepperDescription.vue0.5 kB
  • app/components/ui/stepper/StepperStep.vue2.8 kB
  • app/components/ui/stepper/context.ts0.6 kB
  • app/components/ui/stepper/stepper.variants.ts1.1 kB
  • app/components/ui/stepper/types.ts0.2 kB
  • app/components/ui/stepper/index.ts0.9 kB

Raw manifest:https://uipkge.dev/r/vue/stepper.json