- ProfileName and avatar
- WorkspaceInvite your team
- ReviewConfirm details
<Stepper
ariaLabel="Setup progress"
defaultActiveIndex={1}
size="sm"
variant="inline"
>
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" />
<Stepper.Item label="Workspace" description="Invite your team" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>Features
- Shows progress through a flow as numbered steps.
- Marks steps as completed, current, upcoming, or error.
- Lets people go back to completed steps.
- Supports horizontal, stacked, and vertical layouts in two sizes.
- Shows step content under the active step in vertical layouts.
- Supports controlled and uncontrolled active steps.
Anatomy
<Stepper ariaLabel="Setup progress">
<Stepper.Item label="Account" />
<Stepper.Item label="Profile" />
</Stepper>Sizes
Stepper has two sizes: sm and md. The default is sm. Size scales the indicators, labels, and descriptions.
- ProfileName and avatar
- ReviewConfirm details
- ProfileName and avatar
- ReviewConfirm details
import { Stack, Stepper } from "@vortexlabs/vortex";
<Stack gap={8}>
<Stepper ariaLabel="Small stepper" defaultActiveIndex={1} size="sm">
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
<Stepper ariaLabel="Medium stepper" defaultActiveIndex={1} size="md">
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
</Stack>Variants
inline places the step text beside the indicator; stacked places it underneath. The default is inline.
- ProfileName and avatar
- ReviewConfirm details
- ProfileName and avatar
- ReviewConfirm details
<Stack gap={8}>
<Stepper ariaLabel="Inline stepper" defaultActiveIndex={1} variant="inline">
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
<Stepper ariaLabel="Stacked stepper" defaultActiveIndex={1} variant="stacked">
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
</Stack>Vertical Orientation
Set orientation="vertical" to run steps top to bottom. A step's children show only while it is active or has an error. Vertical steppers ignore variant.
- Install the packageAdd Vortex to your projectRun bun add @vortexlabs/vortex in your project root.
- Add the providerWrap your appRender VortexProvider at the root.
- Import the stylesLoad the theme CSSImport your theme's globals.css once.
- Build somethingUse your first componentTry rendering a Button.
"use client";
import { Button, Stack, Stepper, Text } from "@vortexlabs/vortex";
import React from "react";
const steps = [
{ label: "Install the package", description: "Add Vortex to your project", content: "Run bun add @vortexlabs/vortex in your project root." },
{ label: "Add the provider", description: "Wrap your app", content: "Render VortexProvider at the root." },
{ label: "Import the styles", description: "Load the theme CSS", content: "Import your theme's globals.css once." },
{ label: "Build something", description: "Use your first component", content: "Try rendering a Button." },
];
export const StepperVerticalExample = () => {
const [activeIndex, setActiveIndex] = React.useState(0);
const contentRefs = React.useRef<(HTMLDivElement | null)[]>([]);
const focusContentRef = React.useRef(false);
// Next collapses with its step, so move focus to the new step's content
React.useEffect(() => {
if (!focusContentRef.current) return;
focusContentRef.current = false;
contentRefs.current[activeIndex]?.focus();
}, [activeIndex]);
const handleNext = (index: number) => {
focusContentRef.current = true;
setActiveIndex(index + 1);
};
return (
<Stepper
ariaLabel="Onboarding progress"
orientation="vertical"
activeIndex={activeIndex}
onChange={({ index }) => setActiveIndex(index)}
>
{steps.map((step, index) => (
<Stepper.Item key={step.label} label={step.label} description={step.description}>
<div
ref={(element) => {
contentRefs.current[index] = element;
}}
tabIndex={-1}
style={{ outline: "none" }}
>
<Stack gap={3} align="start">
<Text size="body-sm-desktop">{step.content}</Text>
{index < steps.length - 1 && (
<Button size="sm" onClick={() => handleNext(index)}>
Next
</Button>
)}
</Stack>
</div>
</Stepper.Item>
))}
</Stepper>
);
};Hidden Text
Set textHidden to show only the indicators. The labels stay available to assistive technology.
- Profile
- Workspace
- Review
<Stepper ariaLabel="Setup progress" defaultActiveIndex={1} textHidden>
<Stepper.Item label="Account" />
<Stepper.Item label="Profile" />
<Stepper.Item label="Workspace" />
<Stepper.Item label="Review" />
</Stepper>Step Status
Steps before the active step are completed; later steps are upcoming. Set status on Stepper.Item to mark a step as error, or as completed when people already finished a later step.
- WorkspaceInvite your team
- ReviewConfirm details
- AccountEmail and password
- ReviewConfirm details
<Stack gap={8}>
<Stepper ariaLabel="Setup progress" activeIndex={2}>
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name is missing" status="error" />
<Stepper.Item label="Workspace" description="Invite your team" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
<Stepper ariaLabel="Edit progress" activeIndex={0}>
<Stepper.Item label="Account" description="Email and password" />
<Stepper.Item label="Profile" description="Name and avatar" status="completed" />
<Stepper.Item label="Workspace" description="Invite your team" status="completed" />
<Stepper.Item label="Review" description="Confirm details" />
</Stepper>
</Stack>Clickable Steps
Completed and error steps are clickable by default, so people can go back. Set clickable to allow skipping ahead, or clickable={false} to lock a step. The current step is never clickable.
- Account
- AccountLocked
- Workspace
- Review
<Stack gap={8}>
<Stepper ariaLabel="Open navigation" defaultActiveIndex={0}>
<Stepper.Item label="Account" />
<Stepper.Item label="Profile" clickable />
<Stepper.Item label="Workspace" clickable />
<Stepper.Item label="Review" clickable />
</Stepper>
<Stepper ariaLabel="Locked progress" defaultActiveIndex={2}>
<Stepper.Item label="Account" description="Locked" clickable={false} />
<Stepper.Item label="Profile" />
<Stepper.Item label="Workspace" />
<Stepper.Item label="Review" />
</Stepper>
</Stack>Controlled Selection
Set activeIndex and update it in onChange to drive the steps from your flow. Setting it past the last step marks every step as completed.
- Cart
- Shipping
- Payment
- Review
"use client";
import { Button, Stack, Stepper, Text } from "@vortexlabs/vortex";
import React from "react";
const steps = ["Cart", "Shipping", "Payment", "Review"];
export const StepperControlledExample = () => {
const [activeIndex, setActiveIndex] = React.useState(0);
const placed = activeIndex === steps.length;
return (
<Stack gap={6} width="100%">
<Stepper
ariaLabel="Checkout progress"
activeIndex={activeIndex}
onChange={({ index }) => setActiveIndex(index)}
>
{steps.map((label) => (
<Stepper.Item key={label} label={label} />
))}
</Stepper>
<Text size="body-md-desktop">
{placed ? "Order placed. Thank you!" : `Step ${activeIndex + 1}: ${steps[activeIndex]}`}
</Text>
<Stack direction="row" gap={2}>
<Button
variant="secondary"
disabled={activeIndex === 0 || placed}
onClick={() => setActiveIndex((index) => index - 1)}
>
Back
</Button>
<Button disabled={placed} onClick={() => setActiveIndex((index) => index + 1)}>
{activeIndex === steps.length - 1 ? "Place order" : "Continue"}
</Button>
</Stack>
</Stack>
);
};Use ProgressBar for progress without named steps, and Tabs for views people can switch between in any order.
Accessibility
description.ariaLabel to name the step list.