Getting Started
Installation
Install the core package and its peer dependencies:
pnpm add react-formsteps-core react-hook-form zod @hookform/resolversOptionally, install the UI components package:
pnpm add react-formsteps-uiYour first multi-step form
1. Define your schemas
Each step has its own Zod schema. Only the current step's schema is validated when the user clicks "Next".
import { z } from 'zod';
const step1 = z.object({
firstName: z.string().min(1, 'Required'),
lastName: z.string().min(1, 'Required'),
});
const step2 = z.object({
email: z.string().email('Enter a valid email'),
});
const step3 = z.object({
password: z.string().min(8),
});2. Use the headless hooks
import { useSteps, useStepForm } from 'react-formsteps-core';
function RegistrationForm() {
const schemas = [step1, step2, step3];
const { currentStep, next, prev, isFirst, isLast, progress, totalSteps } = useSteps({
totalSteps: schemas.length,
});
const { form, nextWithValidation, isValidating } = useStepForm({
schema: schemas[currentStep],
});
const handleNext = async () => {
const ok = await nextWithValidation();
if (ok && !isLast) next();
if (ok && isLast) handleSubmit(form.getValues());
};
return (
<form>
<div>Step {currentStep + 1} of {totalSteps}</div>
<progress value={progress} max={100} />
{currentStep === 0 && (
<>
<input {...form.register('firstName')} placeholder="First name" />
<input {...form.register('lastName')} placeholder="Last name" />
</>
)}
{/* ... other steps */}
<button type="button" disabled={isFirst} onClick={prev}>Back</button>
<button type="button" onClick={handleNext} disabled={isValidating}>
{isLast ? 'Submit' : 'Next'}
</button>
</form>
);
}3. (Optional) Use the UI components
For a quicker setup, use the pre-built UI components:
import { Steps, Step, StepBar, StepNav } from 'react-formsteps-ui';
function RegistrationForm() {
return (
<Steps schemas={[step1, step2, step3]} onSubmit={(data) => console.log(data)}>
<Step title="Personal Info">
{/* your fields */}
</Step>
<Step title="Contact">
{/* your fields */}
</Step>
<Step title="Security">
{/* your fields */}
</Step>
</Steps>
);
}TypeScript
All APIs are fully typed. Enable strict: true in your tsconfig.json for the best experience.
{
"compilerOptions": {
"strict": true
}
}