Stepper
A multi-step flow indicator and controller for onboarding, checkout, and wizards. Controlled by a 0-based index; each step is automatically completed, current, or upcoming. Not Tabs — this is an ordered linear flow.
Wizard with Back / Next
StepperNav holds the Step indicators;
StepPanel renders only when its index is active.
With clickable, completed steps are clickable to go back.
Step 1 — create your account.
import { useState } from "react";
import { Stepper, StepperNav, Step, StepPanel, Stack, Button, Text } from "@usevyre/react";
const [step, setStep] = useState(0);
const last = 2;
<Stepper value={step} onChange={setStep} clickable>
<StepperNav>
<Step index={0} label="Account" description="Email & password" />
<Step index={1} label="Profile" description="Your details" />
<Step index={2} label="Done" description="Review" />
</StepperNav>
<StepPanel index={0}>
<Text>Step 1 — create your account.</Text>
</StepPanel>
<StepPanel index={1}>
<Text>Step 2 — fill in your profile.</Text>
</StepPanel>
<StepPanel index={2}>
<Text>Step 3 — review and finish. 🎉</Text>
</StepPanel>
<Stack direction="row" gap="sm" justify="between">
<Button variant="ghost" onClick={() => setStep((s) => s - 1)} disabled={step === 0}>
Back
</Button>
<Button
variant="primary"
onClick={() => setStep((s) => Math.min(s + 1, last))}
disabled={step === last}
>
{step === last ? "Finish" : "Next"}
</Button>
</Stack>
</Stepper> <script setup>
import { ref } from "vue";
import { Stepper, StepperNav, Step, StepPanel, Stack, Button, Text } from "@usevyre/vue";
const step = ref(0);
const last = 2;
</script>
<template>
<Stepper v-model="step" clickable>
<StepperNav>
<Step :index="0" label="Account" description="Email & password" />
<Step :index="1" label="Profile" description="Your details" />
<Step :index="2" label="Done" description="Review" />
</StepperNav>
<StepPanel :index="0">
<Text>Step 1 — create your account.</Text>
</StepPanel>
<StepPanel :index="1">
<Text>Step 2 — fill in your profile.</Text>
</StepPanel>
<StepPanel :index="2">
<Text>Step 3 — review and finish. 🎉</Text>
</StepPanel>
<Stack direction="row" gap="sm" justify="between">
<Button variant="ghost" :disabled="step === 0" @click="step--">Back</Button>
<Button
variant="primary"
:disabled="step === last"
@click="step = Math.min(step + 1, last)"
>
{{ step === last ? "Finish" : "Next" }}
</Button>
</Stack>
</Stepper>
</template> Vertical orientation
Set orientation="vertical" for a stacked layout — common for
checkout sidebars and onboarding panels. The connector runs down the side
instead of across.
import { useState } from "react";
import { Stepper, StepperNav, Step, Stack, Button } from "@usevyre/react";
const [step, setStep] = useState(1);
<Stepper orientation="vertical" value={step} onChange={setStep} clickable>
<StepperNav>
<Step index={0} label="Cart" description="2 items" />
<Step index={1} label="Shipping" description="Enter address" />
<Step index={2} label="Payment" description="Card details" />
<Step index={3} label="Review" description="Confirm order" />
</StepperNav>
<Stack direction="row" gap="sm" justify="end">
<Button variant="ghost" onClick={() => setStep((s) => s - 1)} disabled={step === 0}>
Back
</Button>
<Button
variant="primary"
onClick={() => setStep((s) => Math.min(s + 1, 3))}
disabled={step === 3}
>
Next
</Button>
</Stack>
</Stepper> <script setup>
import { ref } from "vue";
import { Stepper, StepperNav, Step, Stack, Button } from "@usevyre/vue";
const step = ref(1);
</script>
<template>
<Stepper orientation="vertical" v-model="step" clickable>
<StepperNav>
<Step :index="0" label="Cart" description="2 items" />
<Step :index="1" label="Shipping" description="Enter address" />
<Step :index="2" label="Payment" description="Card details" />
<Step :index="3" label="Review" description="Confirm order" />
</StepperNav>
<Stack direction="row" gap="sm" justify="end">
<Button variant="ghost" :disabled="step === 0" @click="step--">Back</Button>
<Button
variant="primary"
:disabled="step === 3"
@click="step = Math.min(step + 1, 3)"
>
Next
</Button>
</Stack>
</Stepper>
</template> Props
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Controlled active step (0-based). Omit for uncontrolled. |
defaultValue | number | 0 | Initial active step when uncontrolled. |
onChange | (index: number) => void | — | Emits the new active index. Not an event. |
orientation | "horizontal" | "vertical" | "horizontal" | Layout direction. |
clickable | boolean | false | Click a completed Step to jump back to it. |
Step props
Step goes inside StepperNav;
StepPanel takes a matching index and shows its
content when active. Both require an explicit 0-based index.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | — | 0-based position (required). |
label | ReactNode | — | Step label. |
description | ReactNode | — | Secondary text under the label. |
icon | ReactNode | — | Custom indicator (defaults to number, ✓ when completed). |
Valid props
| Prop | Values | Default |
|---|---|---|
orientation | "horizontal"|"vertical" | horizontal |
clickable | true|false | false |
Common AI mistakes
- Using Tabs for a wizard / checkout flow→ Use <Stepper> with StepperNav + Step + StepPanel
- onChange={(e) => set(e.target.value)}→ onChange={(index) => setStep(index)}
- Manually toggling which panel is visible→ Give each StepPanel an index; Stepper shows the active one
- <Step> or <StepPanel> outside <Stepper>→ Nest Step inside StepperNav, StepPanel inside Stepper
Quick examples
const [step, setStep] = useState(0);
<Stepper value={step} onChange={setStep}>
<StepperNav>
<Step index={0} label="Account" />
<Step index={1} label="Profile" />
<Step index={2} label="Done" />
</StepperNav>
<StepPanel index={0}><AccountForm /></StepPanel>
<StepPanel index={1}><ProfileForm /></StepPanel>
<StepPanel index={2}><Summary /></StepPanel>
<Stack direction="row" gap="sm" justify="between">
<Button onClick={() => setStep((s) => s - 1)} disabled={step === 0}>Back</Button>
<Button variant="primary" onClick={() => setStep((s) => s + 1)}>Next</Button>
</Stack>
</Stepper><Stepper orientation="vertical" defaultValue={1}>
<StepperNav>
<Step index={0} label="Cart" description="2 items" />
<Step index={1} label="Shipping" description="Enter address" />
<Step index={2} label="Payment" />
</StepperNav>
</Stepper>