Hook
Access and control your stepper with the useStepper hook
The useStepper hook returns the stepper instance with a structured API: state, navigation, lookup, flow, metadata, and lifecycle. Inside <Scoped> or Stepper.Root, useStepper() (with no arguments) reads from context. In a component with no provider, useStepper() creates its own stepper state for that component. You can still pass config when outside a provider to set initialStep and initialMetadata.
The hook accepts an optional single argument: a config object with:
initialStep— The ID of the initial step (must be one of your step IDs).initialMetadata— Partial record of step ID → metadata; used to seedmetadata.get(id)andstate.current.metadataper step.
Usage
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ();
return (
<>
<>{....}</>
< ={() => ..()} ={..}>Next</>
< ={() => ..()} ={..}>Previous</>
</>
);
};Rendering methods
The stepper exposes flow with helpers to render content by step: flow.is, flow.when, flow.switch, and flow.match.
when
The flow.when method allows rendering content conditionally based on the current step. It takes a step ID (string or array of ID plus conditions), a whenFn (the function to run if the step matches), and an optional elseFn (run when it does not match).
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ();
return (
<>
{..("first", () => (
<>First step: {.}</>
))}
{..("second", () => (
<>Second step: {.}</>
))}
</>
);
};You can pass an optional elseFn to render when the current step does not match:
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return (
<>
{..(
"review",
() => <>You're on review: {.}</>,
() => <>Other step: {.}</>
)}
</>
);
}You can also use an array as the first argument: the first element is the step ID, the rest are boolean conditions. The step matches only when the ID matches and all conditions are true. This allows multi-condition logic (e.g. step + feature flag or validation state).
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ();
const = true;
const = true;
return (
<>
{..(["first", , ], () => (
<>First step (editable): {.}</>
))}
{..("second", () => (
<>Second step: {.}</>
))}
</>
);
};switch
The flow.switch method lets you render content based on the current step ID in a switch-case style. It is a cleaner and more scalable way to handle many steps without multiple when conditions.
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ();
return (
<>
{..({
: () => <>First: {.}</>,
: () => <>Second: {.}</>,
})}
</>
);
};match
The flow.match method lets you render content based on an external step ID (e.g. from the server, URL, or other state). Unlike flow.switch, which uses the stepper’s current step, flow.match takes that ID as the first argument. Useful for frameworks like Remix with server-side state or when driving the UI from a URL param.
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ();
const = "first"; // e.g. from server or URL
return (
<>
{..(, {
: () => <>First: {.}</>,
: () => <>Second: {.}</>,
})}
</>
);
};is
For simple conditionals, use flow.is(id). It returns true when the current step’s ID equals id.
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "form", : "Form" },
{ : "confirmation", : "Confirmation" }
);
function () {
const = ();
return (
<>
{..("confirmation") && <>Order summary here.</>}
{..("form") && <>Fill the form.</>}
</>
);
}State and navigation
| Property / Method | Description |
|---|---|
state.all | Array of all step objects |
state.current | { data, index, status, metadata } — current step; use state.current.metadata.get/set/reset for current step only |
state.isFirst / state.isLast | true when on first or last step |
state.isTransitioning | true while a transition (with lifecycle callbacks) is in progress |
navigation.next() | Go to next step |
navigation.prev() | Go to previous step |
navigation.goTo(id) | Go to step by ID |
navigation.reset() | Reset to initial step |
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return (
<>
<>Step {... + 1}: {....}</>
< ="button" ={() => ..()} ={..}>Back</>
< ="button" ={() => ..()} ={..}>Next</>
< ="button" ={() => ..("review")}>Jump to Review</>
< ="button" ={() => ..()}>Reset</>
</>
);
}Lookup
Step lookup helpers (same shape as generateStepperUtils from @stepperize/core):
| Method | Description |
|---|---|
lookup.getAll() | All steps |
lookup.get(id) | Step object by ID |
lookup.getIndex(id) | Index of step by ID |
lookup.getByIndex(index) | Step at index |
lookup.getFirst() / lookup.getLast() | First / last step |
lookup.getNext(id) / lookup.getPrev(id) | Next / previous step after id |
lookup.getNeighbors(id) | { prev, next } for step id |
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
const = ..("review");
const = ..(....);
return (
<>Review: {?.}; neighbors: {.?. ?? "none"} → {.?. ?? "none"}</>
);
}Transition hooks (before / after)
For actions before or after a transition (e.g. validation, analytics), use stepper.lifecycle.onBeforeTransition(cb) and stepper.lifecycle.onAfterTransition(cb). They run on every next(), prev(), and goTo(). Return false (or a promise that resolves to false) from onBeforeTransition to cancel the transition.
The callback receives a TransitionContext with: from, to, metadata, statuses, direction ("next" | "prev" | "goTo"), fromIndex, toIndex. Use direction to branch (e.g. run logic only before "next" or "prev").
onBeforeTransition
Runs before the step index updates. Return false to cancel.
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First" },
{ : "second", : "Second" }
);
function () {
const = ();
React.(() => {
..(async () => {
if (. === "prev" && !.("Go back?")) return false;
});
}, []);
return (
< ="button" ={() => ..()} ={..}>
Next
</>
);
}onAfterTransition
Runs after the step index has updated.
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First" },
{ : "second", : "Second" }
);
function () {
const = ();
React.(() => {
..(() => {
.(`${..} → ${..}`);
});
}, []);
return (
< ="button" ={() => ..()} ={..}>
Next
</>
);
}Metadata
Metadata lets you attach custom data per step (e.g. form drafts, values from the server). You can set initial metadata in the hook config and read/write it at runtime.
Initial metadata
Pass initialMetadata in useStepper({ ... }) to seed metadata per step:
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" }
);
const = () => {
const = ({
: {
: { : "1" },
},
});
const = ..("first");
return <>First step: {?.value}</>;
};set / get / reset
metadata.set(id, values)— Set metadata for a step.metadata.get(id)— Get metadata for a step.metadata.reset(keepInitialMetadata?)— Reset all metadata; passtrueto keep initial values from config.
Current step only: state.current.metadata.get(), state.current.metadata.set(values), state.current.metadata.reset().
import * as React from "react";
import { } from "@stepperize/react";
const { } = (
{ : "first", : "First step" },
{ : "second", : "Second step" },
{ : "last", : "Last step" }
);
const = () => {
const = ();
const = () => {
..("first", { : "saved" });
};
const = ..("first");
return (
<>
< ="button" ={}>Save</>
<>First step: {?.value}</>
< ="button" ={() => ..(true)}>Reset metadata</>
</>
);
};API reference
| Name | Type | Description |
|---|---|---|
state | StepperState<Steps> | all, current (data, index, status, metadata), isFirst, isLast, isTransitioning |
navigation | StepperNavigation<Steps> | next(), prev(), goTo(id), reset() |
lookup | StepperLookup<Steps> | getAll(), get(id), getIndex(id), getByIndex(index), getFirst() / getLast(), getNext(id) / getPrev(id), getNeighbors(id) |
flow | StepperFlow<Steps> | is(id), when(id, whenFn, elseFn?), switch(when), match(state, matches) |
metadata | StepperMetadata<Steps> | values, set(id, values), get(id), reset(keepInitialMetadata?) |
lifecycle | { onBeforeTransition, onAfterTransition } | Register callbacks for every transition; return false from onBeforeTransition to cancel |
Last updated on