Stepper
<Stepper> is the controller for multi-step linear flows. Renders the indicator at the top, body for the active step, and exposes useStepper() to drive Next / Back from inside the body. For branching flows, use a state machine plus your own UI — Stepper is intentionally linear.
When to use
- Onboarding flows (“Welcome”, “Pick a plan”, “Invite team”).
- “Create agent” wizards.
- Any 3–6 step linear-progression UI.
Don’t use Stepper for: branching flows (use a state machine), single-step forms (just render the form), or flows where the user should be able to skip arbitrary steps (use Tabs instead).
Default
Give the agent a name and a one-sentence purpose.
function Footer() { const { back, next, isFirst, isLast } = useStepper(); return ( <div className="flex justify-between"> <Button variant="outline" onClick={back} disabled={isFirst}>Back</Button> <Button onClick={next} disabled={isLast}>{isLast ? 'Submit' : 'Next'}</Button> </div> );}
<Stepper steps={[ { id: 'basics', label: 'Basics' }, { id: 'tools', label: 'Tools' }, { id: 'review', label: 'Review' }, ]} defaultActive="basics"> <StepperHeader /> <StepperContent step="basics">…</StepperContent> <StepperContent step="tools">…</StepperContent> <StepperContent step="review">…</StepperContent> <Footer /></Stepper>API
Stepper
| Prop | Type | Default | Description |
|---|---|---|---|
steps * | StepDescriptor[] | — | Each: { id, label, description?, disabled? }. |
active | string | — | Controlled active step id. Pair with onActiveChange. |
defaultActive | string | — | Initial active step id (uncontrolled). |
onActiveChange | (id: string) => void | — | Fires when the active step changes. |
allowJumpBack | boolean | true | Whether visited steps are clickable in the header to go back. |
orientation | "horizontal" | "vertical" | "horizontal" | Visual orientation. |
StepperHeader / StepperContent
StepperHeader renders the indicator with all steps. StepperContent requires a step prop matching one of the descriptor ids; it only renders when its step is active.
useStepper()
Returns { activeIndex, activeStep, isFirst, isLast, next, back, goto(id) }.
Design guidelines
✓ Do
- Validate the current step before calling next() — you own the gate.
- Surface progress in the header. Visual progress is a contract; users abandon flows without it.
- Pre-fill defaults for "Review" steps; nobody likes re-typing what they just confirmed.
✗ Don't
- Skip steps in linear order. Use a state machine + your own UI for branches.
- Allow forward jumps to unvisited steps. The forward gate exists to enforce validation.
- Hide the Back button on step 2. Going back is the most-used affordance after Next.
Accessibility
- Header is
role="tablist"; each step trigger isrole="tab"witharia-selected. - Disabled / unreachable steps set
aria-disabledandtabIndex={-1}. - Content panels are
role="tabpanel"linked to their tab.