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 objectstepper.state.current.data
Current step indexstepper.state.current.index
Current step statusstepper.state.current.status
Metadata for current stepstepper.state.current.metadata.get() / .set(values) / .reset()
All stepsstepper.state.all (not stepper.steps)
First/Last/Transitioningstepper.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 steps
  • lookup.get(id) — step by ID
  • lookup.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.

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 ID
  • initialMetadata? — initial metadata per step
  • children

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 every next / prev / goTo. Return false (or a promise that resolves to false) 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?, and children as render prop ({ stepper }) => ... or node.
  • Stepper.List, Stepper.Item — list structure; each Item takes a step (step ID).
  • Stepper.Trigger, Stepper.Indicator, Stepper.Title, Stepper.Description — trigger and label content.
  • Stepper.Separator — between steps.
  • Stepper.Content — panel per step; requires step prop; 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

Areav5v6
defineStepperPossibly second argumentdefineStepper(...steps) only
useStepper configinitial: { step, metadata, statuses }initialStep?, initialMetadata?
Current stepe.g. currentStepstate.current.data
All stepse.g. steps on stepperstate.all
Step by IDe.g. get(id) at top levellookup.get(id)
Scoped propsinitial objectinitialStep?, initialMetadata?
Transition callbacksConfig or globallifecycle.onBeforeTransition / onAfterTransition
Conditional by stepwhen / switch+ flow.is(id)
UI components--Stepper.Root, .List, .Item, etc.

Full API reference

For the complete v6 API, see:

Edit on GitHub

Last updated on

On this page