Status vs completion
Understand how step status and completion differ.
This is the concept that trips people up most, so let's make it concrete. A step has two unrelated states, and Stepperize tracks them separately.
Move the active step (click a circle) and toggle completion (the badges) independently. Notice they never affect each other.
Two independent axes
Click a step to move. Click a completion cell to toggle it. Only one row follows the position.
| Axis | ||||
|---|---|---|---|---|
status(id)derived from position | previous | active | upcoming | upcoming |
isComplete(id)set by you | ||||
Status is positional. Move the active step and every cell in the row recolors, with no extra calls.
Completion is business state. A previous step is not automatically complete, and moving never clears it.
Two axes, never linked
| Status | Completion | |
|---|---|---|
| What it is | Where the step sits relative to the active one | Whether your app considers it done |
| Who sets it | Stepperize, automatically | You, explicitly |
| Values | "previous" · "active" · "upcoming" | true / false |
| Read with | stepper.status(id) | stepper.isComplete(id) |
| Write with | navigation (next, goTo, …) | setComplete(id) / setComplete(id, false) |
The key insight: a "previous" step is not automatically completed. The user walking past a step says nothing about whether that step is valid, submitted, or approved. Only your app knows that.
Why the split exists
Real flows need both:
- Status drives visual position — highlight the current step, dim upcoming ones, draw a line behind finished ones. It's UI sugar, derived for free.
- Completion drives business rules — enable "Submit" only when every step is complete, show a green check after a step is validated, let users jump back to "completed" steps but not "upcoming" ones.
// Positional: style the step list
<Stepper.Item /* data-status="active | previous | upcoming" */ />
// Business: gate the final action
const allDone = stepper.steps.every((s) => stepper.isComplete(s.id));
<button disabled={!allDone}>Place order</button>Common pattern: complete on a valid "next"
Mark a step complete only once it passes its guard:
await stepper.next({ data: formValues, complete: true });That keeps completion meaningful: a step is "done" because it was accepted, not merely because the user scrolled past it.
Next: controlled vs uncontrolled.
Last updated on