React Form Wizard
API Reference

Event Handling

Callbacks fired by the FormWizard component.

Event Handling

EventSignature
onComplete(data?: WizardData) => void — fires when finish is pressed on a valid last step.
onTabChange(e: WizardStepChangeEvent) => void — fires when the active step changes.
onDataChange(data: WizardData) => void — fires whenever wizard data changes.

onComplete

Fires after finish is triggered and the final step validates. If the last step is invalid, navigation is blocked and this never runs — so you do not need to re-check validity inside it.

import type { WizardData } from "react-form-wizard-component";

const handleComplete = (data?: WizardData) => {
  console.log("Wizard completed with data:", data);
};

<FormWizard onComplete={handleComplete}>{/* ... */}</FormWizard>;

With react-hook-form, remember handleSubmit returns an event handler, so invoke it rather than passing it straight through:

<FormWizard onComplete={() => void form.handleSubmit(submit)()} />

onTabChange

Fires whenever navigation changes the active step.

import type { WizardStepChangeEvent } from "react-form-wizard-component";

const handleTabChange = ({
  prevIndex,
  nextIndex,
  stepId,
}: WizardStepChangeEvent) => {
  console.log("Tab changed:", { prevIndex, nextIndex, stepId });
};

<FormWizard onTabChange={handleTabChange}>{/* ... */}</FormWizard>;
FieldTypeDescription
prevIndexnumberIndex the wizard moved away from.
nextIndexnumberIndex the wizard moved to.
stepIdstring | undefinedid of the now-active step, when it declares one.

onTabChange no longer fires on mount. Earlier releases reported a spurious 0 → 0 transition on the first render. If you used that to initialise something, do it directly instead.

onDataChange

Fires whenever wizard data changes — from updateData, setData, or a controlled data prop being written back.

const [data, setData] = React.useState<WizardData>({ plan: "basic" });

<FormWizard data={data} onDataChange={setData} schema={schema} />;

Passing data makes the wizard controlled: it renders what you give it and reports intended changes through onDataChange, exactly like a controlled input. Omit data to let the wizard own its state.

Headless equivalent

useWizard exposes the same callbacks as onStepChange and onDataChange:

const wizard = useWizard({
  stepIds: ["a", "b"],
  onStepChange: (e) => console.log(e),
  onDataChange: (d) => console.log(d),
});

See Props for full signatures, and References for the imperative methods.

On this page