Stepperize v8Explore the changes

defineStepper

Reference the API for defining typed flows.

defineStepper

Creates one typed flow.

import { defineStepper } from "@stepperize/react";

const checkout = defineStepper(
  [
    { id: "shipping", title: "Shipping" },
    { id: "payment", title: "Payment" },
    { id: "review", title: "Review" },
  ],
  {
    defaultStep: "shipping",
    linear: true,
  },
);

Use one definition per flow: checkout, onboarding, survey, setup wizard.

The definition is static. It does not create React state until you call useStepper, render Provider, or render Stepper.Root.

Parameters

ParameterDescription
stepsOrdered step objects. Must contain at least one step, and each step needs a unique id; every other field is yours.
options.defaultStepInitial step id. Defaults to the first step.
options.defaultDataInitial flow data.
options.defaultCompletedInitial completed step ids.
options.linearfalse by default. Set true to restrict goTo, triggers and list keyboard navigation to adjacent/previous steps.

Definition options become defaults for every instance created from this definition. Lifecycle callbacks (beforeStepChange, onStepChange) are instance-only — pass them to useStepper, Provider, or Stepper.Root.

Duplicate literal ids are caught by TypeScript when the step list is a literal tuple. Stepperize also validates ids at runtime, so dynamic or widened step arrays still fail early with a clear error.

Returns

PropertyDescription
stepsOriginal steps in order.
useStepper(options?)Creates an independent local instance.
useStepperContext(selector?, isEqual?)Consumes the nearest matching shared instance.
ProviderContext provider for one shared instance.
StepperBound unstyled primitives.
get(id)Pure step access by id.
at(index)Pure step access by index.
parseStep(value)Narrow an arbitrary value to a known step id, or undefined.
validate(id, value)Validate an arbitrary value against a step's schema (Standard Schema). Schemaless steps always succeed.

Local instance

function Checkout() {
  const stepper = checkout.useStepper();
  return <h2>{stepper.current.title}</h2>;
}

Shared instance

function Page() {
  return (
    <checkout.Provider defaultStep="shipping">
      <CurrentTitle />
      <Actions />
    </checkout.Provider>
  );
}

function CurrentTitle() {
  const stepper = checkout.useStepper();
  return <h2>{stepper.current.title}</h2>;
}

Step access without state

const first = checkout.at(0);
const payment = checkout.get("payment");

Use these in static config, tests, or setup code. Use instance step access helpers inside running UI.

Choosing defaults

const checkout = defineStepper(steps, {
  defaultStep: "shipping",
  defaultData: {
    shipping: savedShippingDraft,
  },
  defaultCompleted: ["shipping"],
  linear: true,
});

Use definition defaults for the normal behavior of the flow. Use useStepper(options), Provider props, or Stepper.Root props for a specific screen or controlled integration.

Common mistakes

MistakeFix
Defining steps inside a component on every renderMove static definitions outside the component.
Passing an empty step arrayDefine at least one step. defineStepper throws early for empty arrays.
Reusing a step idGive every step a unique id. Literal duplicates are type errors when possible; runtime validation catches the rest.
Using labels as idsUse stable ids like "shipping" and keep labels in title.
Expecting defaultStep to control state after mountUse controlled step / onStepChange for external state.
Marking all previous steps complete automaticallyUse setComplete(id) when your app accepts a step.

Keep the definition at module scope and treat the step array and its ids as immutable. Static indexes are cached per definition; mutating steps after definition is unsupported.

Lightweight entry

defineStepper from @stepperize/react/headless accepts the same arguments and returns the same state API, excluding the Stepper UI primitives. Import its StepperDefinition type from the same entry. The main entry also exports this narrower type as HeadlessStepperDefinition.

Choose it for custom markup or React Native. See bundle size and usage.

Edit on GitHub

Last updated on

On this page