Migrating to v6
Guide to migrating from v5 to Stepperize v6
v6 simplifies the API: a single state shape, explicit lifecycle hooks, no persistence or progress tracking, and typed primitives with render props. There is no built-in persistence (e.g. localStorage) or progress tracking; implement yourself if needed. This guide walks through the changes and how to update your code.
defineStepper
Before (v5): v5 may have accepted an optional second argument in some setups.
Now: Only step objects. No second argument.
const { steps, Scoped, useStepper, Stepper } = defineStepper(
{ id: "first", title: "First" },
{ id: "second", title: "Second" }
);See Define for the full API.
useStepper — configuration
Before (v5): Nested initial object with step, metadata, and statuses:
useStepper({
initial: {
step: "second",
metadata: { first: { saved: true } },
statuses: { ... },
},
});Now: Top-level initialStep and initialMetadata. No initial statuses (status is derived from position: active / success / inactive).
useStepper({
initialStep: "second",
initialMetadata: { first: { saved: true } },
});useStepper — state shape
Before (v5): Names like currentStep, steps, get / getStepById at top level or under different keys.
Now: Everything lives under state, navigation, lookup, flow, metadata, and lifecycle:
| v5 (conceptual) | v6 |
|---|---|
| Current step object | stepper.state.current.data |
| Current step index | stepper.state.current.index |
| Current step status | stepper.state.current.status |
| Metadata for current step | stepper.state.current.metadata.get() / .set(values) / .reset() |
| All steps | stepper.state.all (not stepper.steps) |
| First/Last/Transitioning | stepper.state.isFirst, stepper.state.isLast, stepper.state.isTransitioning |
Step-by-ID lookup is under lookup, not at the top level: use stepper.lookup.get(id) instead of stepper.get(id).
See Hook for the full structure.
Lookup
Before: utils from defineStepper
Now: stepper.lookup with the same idea, clearer name:
lookup.getAll()— all stepslookup.get(id)— step by IDlookup.getIndex(id),lookup.getByIndex(index)lookup.getFirst(),lookup.getLast()lookup.getNext(id),lookup.getPrev(id),lookup.getNeighbors(id)
There is no stepper.steps on the stepper object; use stepper.state.all for the list of steps.
See Lookup for details and generateStepperUtils from core when you need lookup without a stepper instance.
Navigation
Now: Same idea, under navigation:
stepper.navigation.next()stepper.navigation.prev()stepper.navigation.goTo(id)stepper.navigation.reset()
If v5 used different names (e.g. goToNextStep), replace them with the names above.
Scoped
Before (v5): initial prop with nested step, metadata, statuses.
Now: Flat props:
initialStep?— initial active step IDinitialMetadata?— initial metadata per stepchildren
No initial object and no statuses prop.
See Scoped.
Transitions and lifecycle
Before: Global or config-level transition callbacks (if you had them).
Now: Register callbacks on the stepper instance:
stepper.lifecycle.onBeforeTransition(cb)— runs before everynext/prev/goTo. Returnfalse(or a promise that resolves tofalse) to cancel.stepper.lifecycle.onAfterTransition(cb)— runs after the transition.
The callback receives a TransitionContext: from, to, metadata, statuses, direction ("next" | "prev" | "goTo"), fromIndex, toIndex.
Transition state: use stepper.state.isTransitioning (not a top-level property).
See Hook — Transition hooks and Types — TransitionContext.
Flow
New in v6: stepper.flow.is(id) for simple conditionals:
if (stepper.flow.is("payment")) {
return <PaymentForm />;
}
// or in JSX
{stepper.flow.is("confirmation") && <Summary />}flow.when, flow.switch, and flow.match continue to work as before.
Primitives
v6 introduces a full set of typed primitives under Stepper:
- Stepper.Root — container; accepts
orientation?,initialStep?,initialMetadata?, andchildrenas render prop({ stepper }) => ...or node. - Stepper.List, Stepper.Item — list structure; each
Itemtakes astep(step ID). - Stepper.Trigger, Stepper.Indicator, Stepper.Title, Stepper.Description — trigger and label content.
- Stepper.Separator — between steps.
- Stepper.Content — panel per step; requires
stepprop; visible only when that step is current. - Stepper.Actions, Stepper.Prev, Stepper.Next — navigation buttons.
Use them inside Scoped (or inside Stepper.Root, which uses Scoped internally). For item-level data (e.g. status, step data) inside Stepper.Item, use useStepItemContext() from @stepperize/react/primitives — it returns the same shape as state.current for that item.
See Primitives for the full reference.
Summary table
| Area | v5 | v6 |
|---|---|---|
| defineStepper | Possibly second argument | defineStepper(...steps) only |
| useStepper config | initial: { step, metadata, statuses } | initialStep?, initialMetadata? |
| Current step | e.g. currentStep | state.current.data |
| All steps | e.g. steps on stepper | state.all |
| Step by ID | e.g. get(id) at top level | lookup.get(id) |
| Scoped props | initial object | initialStep?, initialMetadata? |
| Transition callbacks | Config or global | lifecycle.onBeforeTransition / onAfterTransition |
| Conditional by step | when / switch | + flow.is(id) |
| UI components | -- | Stepper.Root, .List, .Item, etc. |
Full API reference
For the complete v6 API, see:
Last updated on