use-stepper
Linear multi-step flow (wizards, onboarding, checkout) with typed step names.
| Package | @vizejs/composable/use-stepper |
| Own the source | vize lib pull composable:use-stepper |
| Runtime exports | useStepper |
| Gzip budget | 1024 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useStepper } from "@vizejs/composable/use-stepper";
Try it
Review a registration in editable steps
Move through attendee, session, and review screens while retaining the values needed when an earlier step is edited.
What to observe: Edit the attendee, choose a session, and continue to Review. Next disables on the last step. Choose Attendee in the step list to revise the name, then return through Session; both values remain and the review reflects the edit.
index is zero-based and readonly; current is the typed step name. Navigation changes only the cursor. The name and session are independent writable Vue refs, so conditional screens retain their data. goBackTo only moves to an earlier step; the example disables later step buttons. Previous/Next controls expose the first/last boundaries and the step list marks its current item with aria-current. This example navigates and reviews local values without validation or submission. All state is synchronous and SSR-safe; no browser capability or cleanup is required.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { ref, useId } from "vue";
import { useStepper } from "@vizejs/composable/use-stepper";
const wizardId = useId();
const attendee = ref("Ada");
const session = ref("Compiler clinic");
const {
stepNames,
index,
current,
isFirst,
isLast,
isCurrent,
isBefore,
goToNext,
goToPrevious,
goBackTo,
} = useStepper(["attendee", "session", "review"]);
const titles = { attendee: "Attendee", session: "Session", review: "Review" } as const;
</script>
<template>
<div class="composable-example">
<p>Prepare a workshop registration in three steps. Edits stay available when you go back.</p>
<ol aria-label="Registration steps">
<li v-for="step in stepNames" :key="step">
<button
type="button"
:disabled="!isBefore(step) && !isCurrent(step)"
:aria-current="isCurrent(step) ? 'step' : undefined"
@click="() => goBackTo(step)"
>
{{ titles[step] }}
</button>
</li>
</ol>
<output aria-live="polite"
>Step {{ index + 1 }} of {{ stepNames.length }}: {{ titles[current] }}</output
>
<div v-if="current === 'attendee'">
<label :for="`${wizardId}-attendee`">Workshop attendee</label>
<input :id="`${wizardId}-attendee`" v-model="attendee" autocomplete="name" />
</div>
<div v-else-if="current === 'session'">
<label :for="`${wizardId}-session`">Workshop session</label>
<select :id="`${wizardId}-session`" v-model="session">
<option>Compiler clinic</option>
<option>Type checking clinic</option>
<option>Editor tools clinic</option>
</select>
</div>
<div v-else>
<h3>Review registration</h3>
<p>Attendee: {{ attendee || "Not entered" }}</p>
<p>Session: {{ session }}</p>
<p>This is a local review. No registration has been sent.</p>
</div>
<div class="example-actions">
<button type="button" :disabled="isFirst" @click="goToPrevious">Previous step</button>
<button type="button" :disabled="isLast" @click="goToNext">Next step</button>
</div>
<p>Use an earlier step button to edit it. Later steps open with Next step.</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useStepper |
state | experimental | safe | stable | none | web, server, worker, native, desktop, terminal | — | — |
API
useStepper
Linear multi-step flow (wizards, onboarding, checkout) with typed step names. Pass a tuple of names (["account", "billing", "review"] as const, or a plain array literal thanks to const inference) or an object whose keys are the names and whose values carry per-step data; every navigation method only accepts declared names. Synchronous state: SSR-safe and nothing to dispose.
function useStepper<const Steps extends readonly string[]>( steps: Steps, initial?: NoInfer<Steps[number]>, ): Stepper<Steps[number], { readonly [Step in Steps[number]]: Step }>
const wizard = useStepper(["account", "billing", "review"]);
wizard.goTo("billing");
wizard.goTo("shipping"); // type error
useStepper
function useStepper<Steps extends Readonly<Record<string, unknown>>>( steps: Steps, initial?: NoInfer<keyof Steps & string>, ): Stepper<keyof Steps & string, Steps>
useStepper
function useStepper( steps: readonly string[] | Readonly<Record<string, unknown>>, initial?: string, ): Stepper<string, Readonly<Record<string, unknown>>>
Types
Stepper
Stepper state and navigation returned by useStepper.
| Member | Type | Description |
|---|---|---|
stepNames |
readonly Name[] |
Step names in order. |
index |
Readonly<ShallowRef<number>> |
Zero-based index of the current step. |
current |
ComputedRef<Name> |
Name of the current step. |
currentValue |
ComputedRef<Values[Name]> |
Value of the current step (the name itself for array steps). |
next |
ComputedRef<Name | undefined> |
Name of the next step, or undefined on the last. |
previous |
ComputedRef<Name | undefined> |
Name of the previous step, or undefined on the first. |
isFirst |
ComputedRef<boolean> |
Whether the current step is the first. |
isLast |
ComputedRef<boolean> |
Whether the current step is the last. |
at |
(index: number) => Name | undefined |
Step name at index, or undefined. |
get |
<Step extends Name>(step: Step) => Values[Step] |
Value of a step. |
goTo |
(step: Name) => void |
Jump to a step. |
goToNext |
() => void |
Advance one step (no-op on the last). |
goToPrevious |
() => void |
Go back one step (no-op on the first). |
goBackTo |
(step: Name) => boolean |
Go back to an earlier step; later or current steps are ignored. |
isCurrent |
(step: Name) => boolean |
Whether step is the current step. |
isNext |
(step: Name) => boolean |
Whether step is the step right after the current one. |
isPrevious |
(step: Name) => boolean |
Whether step is the step right before the current one. |
isBefore |
(step: Name) => boolean |
Whether step comes before the current step. |
isAfter |
(step: Name) => boolean |
Whether step comes after the current step. |