Stepper
UiStepper renders a horizontal or vertical progress indicator for multi-step flows (checkout, onboarding, wizards). Each step derives its status — upcoming, current, or completed — from where activeStep sits within the steps array. The component is uncontrolled by default: keep activeStep and the updateActiveStep emit in sync to drive it from a parent (v-model style).
Basic usage
<script setup lang="ts">
import { ref } from 'vue'
const activeStep = ref('profile')
const steps = [
{ id: 'account', label: 'Account', description: 'Create your credentials' },
{ id: 'profile', label: 'Profile', description: 'Tell us about yourself' },
{ id: 'billing', label: 'Billing', description: 'Add a payment method' },
{ id: 'confirm', label: 'Confirm', description: 'Review and finish' },
]
</script>
<template>
<UiStepper
:steps="steps"
:active-step="activeStep"
@update-active-step="activeStep = $event"
/>
</template>
Steps before activeStep render as completed with a check mark; the active step gets aria-current="step"; the rest render as upcoming.
Vertical layout
Set vertical to stack steps in a column, useful for sidebars or narrow layouts.
<template>
<UiStepper :steps="steps" :active-step="activeStep" vertical />
</template>
Linear progression
Set linear to block selecting any step ahead of the current one — users can still navigate back to a completed step, but can't skip forward.
<template>
<UiStepper :steps="steps" :active-step="activeStep" linear @update-active-step="activeStep = $event" />
</template>
Step icons
Give a step an icon (Material Symbols code point) to render it instead of the step number. Completed steps always show a check mark regardless of icon.
<script setup lang="ts">
const steps = [
{ id: 'cart', label: 'Cart', icon: '' },
{ id: 'shipping', label: 'Shipping', icon: '' },
{ id: 'payment', label: 'Payment', icon: '' },
{ id: 'done', label: 'Done', icon: '' },
]
</script>
<template>
<UiStepper :steps="steps" active-step="shipping" />
</template>
Disabled step
Mark a step disabled to remove it from keyboard navigation and block selection entirely, regardless of linear.
<template>
<UiStepper
:steps="[
{ id: 'account', label: 'Account' },
{ id: 'profile', label: 'Profile' },
{ id: 'billing', label: 'Billing', disabled: true },
{ id: 'confirm', label: 'Confirm' },
]"
active-step="profile"
/>
</template>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | IStepItem[] | — | Step items: { id, label, description?, icon?, disabled? }. |
activeStep | string | first step | The id of the step that should be active. |
vertical | boolean | false | Renders the stepper as a vertical list. |
linear | boolean | false | Blocks selecting a step ahead of the current one. |
customClass | string | string[] | Record<string, boolean> | null | — | Extra classes for the stepper root element. |
Emits: updateActiveStep with the selected step's id.
UiTabs: arrow keys move roving focus between selectable steps (skipping disabled ones, and — in linear mode — steps ahead of the current one), Home/End jump to the first/last selectable step.