Stepperize v8Explore the changes

Changelog

Review recent documentation and package changes.

Changelog

Release notes for the current Stepperize documentation line.

v8.0.0 — upcoming

  • Test React and interactive examples in Chromium with Vitest Browser Mode; keep core and server tests in Node. Remove jsdom and Testing Library dependencies and recalculate the performance comparison with production React in a real browser.

Breaking changes

  • Require React 18 or 19. useStepper() always creates local state; shared consumers use useStepperContext(selector?, isEqual?) under the matching Provider or Root.
  • Navigation returns NavigationResult: { accepted: true, from, to } or { accepted: false, reason }.
  • goTo respects linear policy. Intentional branches can pass bypassPolicy: true; guards still run.
  • reset() restores mount-time step, data and completion. Replace old reset payloads with keepData / keepCompleted options.
  • createStepperPrimitives is internal. Use each definition's generated Stepper primitives.

Added

  • Atomic source data and completion with next/prev/goTo({ data, complete: true }).
  • defaultCompleted on hooks, Provider and Root; typed functional data.update; typed data in match handlers.
  • Selector subscriptions with optional equality, independent nested scopes, context.signal cancellation and structured rejection reasons.
  • Root orientation inheritance, Content forceMount, explicit data-complete and instance-scoped accessible IDs.

Fixed

  • Same-event duplicate navigation, lost compound writes, stale controlled state/callbacks in child effects, and uncached object selector loops.
  • Cancelled/unmounted async guards cannot commit stale transitions or leak rejected promises.
  • Triggers default to button type inside forms. Static step lookup indexes and linear-time status computation reduce repeated work.

Documentation and examples

  • Separate /docs/v7 and /docs/v8 documentation, version-specific navigation and search, and a compatible /docs/latest alias.

  • New local/shared guide and interactive demo, context-hook reference and v7-to-v8 migration guide.

  • Updated API references, form integrations, FAQ, AI-readable documentation and v8-compatible registry examples.

Read Migrating to v8 before upgrading. These changes are prepared on the v8 branch and are not a published release yet.

v7.0.0

v7 is a deliberate API reset for Stepperize. The goal is to make the stepper instance easier to read, easier to type, and easier to wire into real workflows: forms, guarded transitions, branching flows, URL-driven state, and headless UI.

The short version: define an ordered step list once, then work with one flat instance everywhere.

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

const stepper = checkout.useStepper();

stepper.current;
await stepper.next();
stepper.data.set(values);
stepper.match({
  shipping: () => <ShippingForm />,
  payment: () => <PaymentForm />,
  review: () => <ReviewOrder />,
});

Packages

  • @stepperize/core
  • @stepperize/react

Highlights

  • Flat React instance: use stepper.current, stepper.id, stepper.next(), stepper.match(...), stepper.data, and stepper.setComplete() directly. The old nested namespaces are gone.
  • Ordered step definitions: defineStepper([...]) now receives one array, which makes order explicit and gives the definition room for shared options like defaultStep, defaultData, defaultCompleted, and linear.
  • Async navigation: next, prev, goTo, and reset now resolve to Promise<boolean>, so guarded transitions can block movement and callers can branch on the result.
  • Guarded transitions: beforeStepChange can validate or cancel a move before data is committed. onStepChange runs after an accepted transition.
  • Typed flow data: data replaces legacy metadata and can be inferred from each step's schema using Standard Schema-compatible validators.
  • Explicit completion: positional status (active, previous, upcoming) is separate from business completion (setComplete, isComplete).
  • Controlled state support for step, data, and completed, with matching change handlers for router, URL, store, or persisted-state integrations.
  • Bound, unstyled React primitives are generated from each stepper definition, so headless UI keeps the same step ids, types, ARIA attributes, and navigation behavior as the hook.

Breaking changes

  • defineStepper(stepA, stepB) is now defineStepper([stepA, stepB]).
  • Scoped was renamed to the generated Provider.
  • initialStep is now defaultStep; initialMetadata is now defaultData.
  • metadata.get, metadata.set, and metadata.reset moved to data.get, data.set, and data.reset.
  • Namespaced instance APIs were flattened:
    • stepper.state.current.data -> stepper.current
    • stepper.state.current.id -> stepper.id
    • stepper.state.current.index -> stepper.index
    • stepper.state.all -> stepper.steps
    • stepper.navigation.next() -> stepper.next()
    • stepper.navigation.prev() -> stepper.prev()
    • stepper.navigation.goTo(id) -> stepper.goTo(id)
    • stepper.flow.switch(...) -> stepper.match(...)
    • stepper.flow.when(id, ...) -> stepper.is(id) && ...
  • Lifecycle subscriptions were replaced with instance options: beforeStepChange for guards and onStepChange for accepted transitions.
  • Navigation methods are async. If code branches on whether navigation worked, await the call before checking the result.
  • Status names changed from "success" / "inactive" to "previous" / "upcoming". Use isComplete(id) when you mean "this step is done" instead of "this step appears before the current one."

Features

  • New @stepperize/core package with public types and pure helpers for shared, framework-independent stepper logic.
  • New step map helpers with get, at, indexOf, has, first, last, next, prev, and neighbors.
  • New parseStep helper for narrowing external values, such as URL params or persisted snapshots, into known step ids.
  • New validateStep helper and instance-level validate() method for Standard Schema-compatible step validation.
  • New status helpers for deriving positional step state from the active index.
  • New exhaustive matchStep core utility and React stepper.match(...) API for typed rendering by step id.
  • New transition payloads: pass { data } to next, prev, goTo, or reset to stage current-step data before guards run.
  • New isPending state and canPrev / canNext guards to make async transitions easier to reflect in UI.
  • New canGoTo(id) policy check used by trigger primitives and useful for linear flows.
  • New Stepper.Items primitive for iterating through the typed step list inside a generated primitive tree.

Migration example

// v6
const { useStepper, Scoped } = defineStepper(
  { id: "account", title: "Account" },
  { id: "profile", title: "Profile" },
);
// v7
const accountFlow = defineStepper([
  { id: "account", title: "Account" },
  { id: "profile", title: "Profile" },
]);

const { useStepper, Provider } = accountFlow;
// v6
stepper.navigation.next();
stepper.flow.switch({
  account: () => <Account />,
  profile: () => <Profile />,
});
// v7
await stepper.next();
stepper.match({
  account: () => <Account />,
  profile: () => <Profile />,
});

Migration

Read Migrating to v7 for the full v6 -> v7 API map, common pitfalls, and a copy-paste migration prompt for coding assistants.

Interactive guides

Explore guarded navigation and cancellation, reset options, controlled state, selectors, linear policy and nested ownership through live examples. Each v8 guide includes guided exercises and the exact source of its demo.

Edit on GitHub

Last updated on

On this page