A long form split into steps on top of the Stepper: per-step validation with an error summary, conditional steps, draft saving and autosave, URL resume, a review step with Edit links, and a leave-page warning. Form-library agnostic — each step exposes validate().
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { MultiStepForm, MultiStepFormStepper, MultiStepFormStep, MultiStepFormReview, MultiStepFormFooter } from "@vamos-ui/react/business";
const steps = [
{ id: "identity", title: "Identity", validate: () => (name ? true : "Enter a name.") },
{ id: "tax", title: "Tax details", when: (ctx) => ctx.kind === "company" },
{ id: "review", title: "Review" },
];
<MultiStepForm steps={steps} context={{ kind }} onComplete={save}>
<MultiStepFormStepper />
<MultiStepFormStep id="identity">…</MultiStepFormStep>
<MultiStepFormStep id="tax">…</MultiStepFormStep>
<MultiStepFormStep id="review"><MultiStepFormReview sections={…} /></MultiStepFormStep>
<MultiStepFormFooter submitLabel="Create employee" />
</MultiStepForm>On a step change focus moves to the step heading, announced as "Step 3 of 6: Contract". On a validation error focus goes to the first invalid field (or the summary).
Enter in a single-line field goes to the next step instead of submitting the whole form. Back never loses entered values.
Examples
Conditional step
when(context) hides the Tax step for individuals; the Stepper count and "Step n of m" update.
Inside a Dialog
presentation="dialog" keeps the footer in flow; the compact Stepper fits narrow panels.
Drafts, autosave and URL resume
onSaveDraft + autosave show "Draft saved 2 minutes ago"; syncUrl mirrors the step in the query string; confirmLeave guards the tab.
Vertical with free navigation
Settings-style setup: the Stepper is a sticky side rail with descriptions and linear={false} allows jumping ahead.
API
MultiStepForm
| Prop | Type | Default |
|---|---|---|
steps{ id, title, description?, optional?, when?(ctx), validate?() → true | string | Promise }. A string blocks Next and is shown as the step's error summary. | StepDef[] | — |
value / defaultValue / onValueChangeCurrent step id (controlled or not). | string | — |
contextPassed to when() of conditional steps. | C | — |
onCompleteRuns on Submit. Throw to show the error and stay on the step. | () => Promise<void> | void | — |
linearSteps ahead are reachable only after the earlier ones pass. | boolean | true |
onSaveDraftShows "Save draft" and the saved time. | () => Promise<void> | void | — |
autosaveCalls onSaveDraft on an interval (min 2 s). | { intervalMs: number } | — |
presentationpage: sticky footer on an opaque surface. dialog: footer in flow. | "page" | "dialog" | "page" |
orientationVertical puts the Stepper in a sticky side rail. | "horizontal" | "vertical" | "horizontal" |
syncUrlMirror the step in ?step= (or your key) with history.replaceState. | boolean | { key: string } | false |
confirmLeavebeforeunload warning until the form completes. | boolean | false |
labelsBack / Next / Submit / Save draft / "Step {current} of {total}"… | Partial<MultiStepFormLabels> | — |
MultiStepFormStep
| Prop | Type | Default |
|---|---|---|
idMatches a StepDef id. Hidden (but mounted) while inactive, so uncontrolled values survive Back. | string | — |
hideHeadingRender your own heading; a visually hidden one keeps focus and the announcement. | boolean | false |
keepMountedUnmount inactive steps instead. | boolean | true |
MultiStepFormStepper
| Prop | Type | Default |
|---|---|---|
variantStepper look; completed steps are clickable. | "numbered" | "compact" | "bar" | "dots" | "numbered" |
MultiStepFormReview
| Prop | Type | Default |
|---|---|---|
sectionsSummary per step with an Edit link that jumps back. | { step: string; title?: string; items: { label: string; value: ReactNode }[] }[] | — |
MultiStepFormFooter
| Prop | Type | Default |
|---|---|---|
submitLabelLabel of the last step's primary button. | string | "Submit" |
startContent at the start (Cancel). | ReactNode | — |
useMultiStepForm()
| Prop | Type | Default |
|---|---|---|
returnsDrive the form from custom parts. | { current, index, total, steps, next(), back(), goTo(id), saveDraft(), isFirst, isLast, status, submitting } | — |