Stepper
Accessible, unstyled Stepper primitive for multi-step flows.
|
|
| Package |
@vizejs/ui/stepper |
| Maturity |
stable |
| Own the source |
vize lib pull stepper |
| Requires |
collection, composite-navigation, context, controllable-state, id, typeahead |
| Aliases |
stepper, wizard, multi-step flow, checkout steps |
| Covers |
WAI-ARIA aria-current step, MUI Stepper, Vuetify Stepper, Reka UI Stepper |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Stepper, StepperRoot, StepperContent, StepperItem, StepperList, StepperTrigger } from "@vizejs/ui/stepper";
Or copy the source into your project with vize lib pull stepper (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Uncontrolled checkout stepper with a labelled step list and one panel per step. -->
<script setup lang="ts">
import {
StepperContent,
StepperItem,
StepperList,
StepperRoot,
StepperTrigger,
} from "@vizejs/ui/stepper";
</script>
<template>
<StepperRoot default-value="shipping">
<StepperList aria-label="Checkout steps">
<StepperItem value="shipping" text-value="Shipping" completed>
<StepperTrigger>Shipping</StepperTrigger>
</StepperItem>
<StepperItem value="billing" text-value="Billing">
<StepperTrigger>Billing</StepperTrigger>
</StepperItem>
<StepperItem value="review" text-value="Review">
<StepperTrigger>Review</StepperTrigger>
</StepperItem>
</StepperList>
<StepperContent value="shipping">Enter your shipping address.</StepperContent>
<StepperContent value="billing">Choose a payment method.</StepperContent>
<StepperContent value="review">Confirm your order.</StepperContent>
</StepperRoot>
</template>
API
Stepper
Source: src/families/navigation/stepper/stepper-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Stepper base id. null and undefined select a deterministic fallback. |
modelValue |
StepperValue |
undefined |
Controlled current step value. undefined selects uncontrolled behavior; null clears it. |
defaultValue |
StepperValue |
undefined |
Initial current step for uncontrolled use. undefined selects the first enabled step. |
disabled |
boolean |
false |
Disable every step trigger while preserving the current content panel. |
navigationMode |
StepperNavigationMode |
"linear" |
Whether future steps require every previous enabled step to be completed. |
orientation |
StepperOrientation |
"horizontal" |
Directional layout hint used by arrow-key navigation. |
dir |
StepperDirection |
"ltr" |
Reading direction used for horizontal arrow-key navigation. |
loop |
boolean |
false |
Whether arrow-key navigation wraps at the first and last enabled trigger. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: StepperValue] |
Fired when the current step requests a new controlled value. |
change |
[value: StepperValue, previous: StepperValue, nativeEvent: Event | null] |
Fired after any distinct current-step request. |
Slots
| Slot |
Slot props |
Description |
default |
StepperSlotState |
Compound Stepper children. Receives current value, completion, and navigation state. |
Exposed
| Member |
Type |
Description |
completedValues |
— |
|
currentIndex |
— |
|
dir |
— |
|
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
id |
— |
|
isSelectable |
— |
|
listId |
— |
|
navigationMode |
— |
|
next |
— |
|
orientation |
— |
|
previous |
— |
|
reset |
— |
|
selectValue |
— |
|
setValue |
— |
|
state |
— |
|
value |
— |
|
StepperRoot
Source: src/families/navigation/stepper/stepper-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Stepper base id. null and undefined select a deterministic fallback. |
modelValue |
StepperValue |
undefined |
Controlled current step value. undefined selects uncontrolled behavior; null clears it. |
defaultValue |
StepperValue |
undefined |
Initial current step for uncontrolled use. undefined selects the first enabled step. |
disabled |
boolean |
false |
Disable every step trigger while preserving the current content panel. |
navigationMode |
StepperNavigationMode |
"linear" |
Whether future steps require every previous enabled step to be completed. |
orientation |
StepperOrientation |
"horizontal" |
Directional layout hint used by arrow-key navigation. |
dir |
StepperDirection |
"ltr" |
Reading direction used for horizontal arrow-key navigation. |
loop |
boolean |
false |
Whether arrow-key navigation wraps at the first and last enabled trigger. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: StepperValue] |
Fired when the current step requests a new controlled value. |
change |
[value: StepperValue, previous: StepperValue, nativeEvent: Event | null] |
Fired after any distinct current-step request. |
Slots
| Slot |
Slot props |
Description |
default |
StepperSlotState |
Compound Stepper children. Receives current value, completion, and navigation state. |
Exposed
| Member |
Type |
Description |
completedValues |
— |
|
currentIndex |
— |
|
dir |
— |
|
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
id |
— |
|
isSelectable |
— |
|
listId |
— |
|
navigationMode |
— |
|
next |
— |
|
orientation |
— |
|
previous |
— |
|
reset |
— |
|
selectValue |
— |
|
setValue |
— |
|
state |
— |
|
value |
— |
|
StepperContent
Source: src/families/navigation/stepper/stepper-content.vue
Props
| Prop |
Type |
Default |
Description |
value (required) |
string |
required |
Content value paired with a StepperItem. |
role |
StepperContentRole | null |
"region" |
Optional landmark role for the content. null renders a plain div. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string | null |
undefined |
Space-separated ids that label the panel. null omits the default trigger id. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the panel. |
Slots
| Slot |
Slot props |
Description |
default |
StepperContentSlotState |
Panel contents. Receives current visibility, orientation, and availability state. |
Exposed
| Member |
Type |
Description |
value |
string |
Content value paired with a StepperItem and StepperTrigger. |
current |
boolean |
Whether this content panel is paired with the current step. |
active |
boolean |
Whether this content panel is currently visible. |
completed |
boolean |
Whether the paired step is marked completed. |
disabled |
boolean |
Whether the Stepper root or paired item is disabled. |
orientation |
StepperOrientation |
Directional layout hint inherited from the Stepper root. |
state |
StepperContentState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered content panel element. |
id |
string |
Deterministic id wired from content to trigger. |
triggerId |
string |
Deterministic id of the controlling trigger. |
focusContent |
(options?: FocusOptions) => void |
Move focus to the visible content panel. |
StepperItem
Source: src/families/navigation/stepper/stepper-item.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned list item id. null and undefined select a deterministic fallback. |
value (required) |
string |
required |
Stable step value used for selection and collection identity. |
completed |
boolean |
false |
Mark this step as completed for styling and linear navigation. |
disabled |
boolean |
false |
Disable this step while preserving the rest of the Stepper. |
textValue |
string | null |
undefined |
Explicit text used by future collection search or virtualized trigger labels. |
order |
number |
undefined |
Deterministic order for virtualized, portalled, or server-only steps. |
ariaLabel |
string |
undefined |
Accessible name when no list item text supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label this list item. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe this list item. |
Slots
| Slot |
Slot props |
Description |
default |
StepperItemSlotState |
Step contents. Receives current, completion, and activation state. |
Exposed
| Member |
Type |
Description |
value |
string |
Step value used for current-step state and collection identity. |
index |
number |
Index within all registered StepperItem instances. |
current |
boolean |
Whether this step is the current step. |
completed |
boolean |
Whether this step is marked completed. |
disabled |
boolean |
Whether this step or its root is disabled. |
selectable |
boolean |
Whether this step may be activated under the current navigation mode. |
locked |
boolean |
Whether a linear Stepper is preventing activation of this otherwise enabled step. |
orientation |
StepperOrientation |
Directional layout hint inherited from the Stepper root. |
navigationMode |
StepperNavigationMode |
Whether step activation is linear or free-form. |
state |
StepperItemState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered list item element. |
id |
string |
Deterministic id assigned to the item. |
triggerId |
string |
Deterministic id wired from trigger to content. |
contentId |
string |
Deterministic id wired from content to trigger. |
focus |
(options?: FocusOptions) => boolean |
Move focus to this step's trigger when it can receive focus. |
select |
() => boolean |
Request this step as the current value. |
StepperList
Source: src/families/navigation/stepper/stepper-list.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the step list. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the step list. |
Slots
| Slot |
Slot props |
Description |
default |
StepperListSlotState |
StepperItem children. Receives the current root and list state. |
Exposed
| Member |
Type |
Description |
value |
StepperValue |
Current step value, or null when no step is current. |
completedValues |
readonly string[] |
Values whose StepperItem is marked completed. |
currentIndex |
number |
Current step index within all registered items, or -1 when empty or missing. |
disabled |
boolean |
Whether every trigger is disabled by the root. |
navigationMode |
StepperNavigationMode |
Whether step activation is linear or free-form. |
orientation |
StepperOrientation |
Directional layout hint for ARIA and consumer-owned styles. |
dir |
StepperDirection |
Reading direction used for horizontal keyboard navigation. |
state |
StepperRootState |
Stable state token for styling and tests. |
listId |
string |
Deterministic id assigned to the list. |
element |
HTMLDivElement | null |
Rendered list element. |
focus |
(options?: FocusOptions) => void |
Move focus to the current, active, or first enabled trigger. |
StepperTrigger
Source: src/families/navigation/stepper/stepper-trigger.vue
Props
| Prop |
Type |
Default |
Description |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the trigger. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the trigger. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before this trigger requests selection. Call preventDefault() to keep state unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
StepperTriggerSlotState |
Trigger contents. Receives current, completion, and activation state. |
indicator |
StepperTriggerSlotState |
Optional completion/current indicator controlled by the consumer. |
Exposed
| Member |
Type |
Description |
value |
string |
Step value used for current-step state and collection identity. |
index |
number |
Index within all registered StepperItem instances. |
current |
boolean |
Whether this step is the current step. |
completed |
boolean |
Whether this step is marked completed. |
disabled |
boolean |
Whether this step or its root is disabled. |
selectable |
boolean |
Whether this step may be activated under the current navigation mode. |
locked |
boolean |
Whether a linear Stepper is preventing activation of this otherwise enabled step. |
orientation |
StepperOrientation |
Directional layout hint inherited from the Stepper root. |
navigationMode |
StepperNavigationMode |
Whether step activation is linear or free-form. |
state |
StepperItemState |
Stable state token for styling and tests. |
element |
HTMLButtonElement | null |
Rendered native trigger button. |
id |
string |
Deterministic id wired from trigger to content. |
contentId |
string |
Deterministic id of the controlled content panel. |
focus |
(options?: FocusOptions) => void |
Move focus to the native trigger button. |
select |
() => boolean |
Request this step as the current value. |
Behavior
Normative state x input -> outcome table for stepper-root.vue,
stepper-list.vue, stepper-item.vue, stepper-trigger.vue, and
stepper-content.vue (@vizejs/ui/stepper). Every row in this isolated slice
is proven by the named focused tests; registry, package exports, renderer
fixtures, and size budgets are intentionally left for the parent integration
step.
| ID |
State |
Input |
Outcome |
Evidence |
| S1 |
default / current |
render |
root, list, item, trigger, and content expose deterministic ids, ARIA wiring, slots, and data |
renders accessible stepper semantics with deterministic ids, slots, and data |
| S2 |
linear / future pending |
click |
focusable future triggers advertise aria-disabled and do not emit or change value until prior steps complete |
linear navigation prevents future activation until prior enabled steps are complete |
| S3 |
linear / reset default |
reset |
imperative reset restores the configured default even when normal linear activation is currently locked |
reset restores the configured default value even when linear activation is locked |
| S4 |
dynamic item value |
prop update |
item registration, trigger ids, content ids, and fallback current value update when a step value changes |
items reregister when their step value changes |
| S5 |
free / disabled item |
Arrow key, then Enter |
roving focus skips natively disabled steps and native keyboard activation selects any enabled step |
free navigation and roving focus can activate any enabled step while skipping disabled |
| S6 |
controlled |
click |
emits the requested value while rendered current state stays controlled until parent accepts it |
controlled current value wins until the parent accepts the request |
| S7 |
disabled root / item |
click or Tab |
disabled triggers leave activation and sequential focus while preserving content state |
disabled roots and items suppress user activation and sequential focus |
| S8 |
exposed instances |
focus, next, previous |
public refs expose element state and imperative focus/value methods |
exposes typed state and imperative focus/value controls |
| S9 |
optional content role |
render |
content can render without a landmark role or default trigger label |
content can opt out of the region role and default trigger label |
| S10 |
missing provider |
setup |
compound parts fail closed with the shared context diagnostic |
compound parts require matching Stepper providers |
| S11 |
SSR and hydration |
isolated render/mount |
generated ids are byte-identical per request and hydrate without replacement warnings |
stepper-ssr.test.ts |
navigationMode="linear" allows the current step, previous enabled steps, and
future enabled steps only when every prior enabled step is marked completed.
Linear-locked future triggers remain roving-focusable with aria-disabled so
the flow remains discoverable; true root or item disabled states use native
disabled buttons and are skipped by keyboard navigation. Content panels retain
stable markup for hydration, use hidden while inactive, and default
aria-labelledby to the paired trigger id when a landmark role is present.