Multi-step Registration Form - React (CRA) Form Validation
Multi-step Registration Form
Build a registration wizard where users can't continue to the next step until the current step is valid.
Instead of validating the entire form at once, validate only the fields that belong to the active step. When the final step is reached, submit the entire form normally.
Complete example
HTML
<form id="signupForm">
<!-- Step 1 -->
<section id="step-1">
<input
type="email"
name="email"
placeholder="Email"
required
/>
<input
type="password"
name="password"
required
minlength="8"
placeholder="Password"
/>
<button
type="button"
id="step1Next"
>
Continue
</button>
</section>
<!-- Step 2 -->
<section
id="step-2"
hidden
>
<input
type="text"
name="displayName"
maxlength="40"
required
placeholder="Display name"
/>
<textarea
name="bio"
maxlength="300"
placeholder="Tell us about yourself"
></textarea>
<button
type="button"
id="step2Back"
>
Back
</button>
<button
type="button"
id="step2Next"
>
Continue
</button>
</section>
<!-- Step 3 -->
<section
id="step-3"
hidden
>
<label>
<input
type="checkbox"
name="terms"
required
/>
I agree to the Terms of Service
</label>
<button
type="button"
id="step3Back"
>
Back
</button>
<button type="submit">
Create account
</button>
</section>
</form>
JavaScript
import NFSFU234FormValidation from "@nfsfu234/form-validation";
NFSFU234FormValidation.configureForms([
{
form: "#signupForm",
fields: {
email: {
required: true,
type: "email",
},
password: {
required: true,
minLength: 8,
},
displayName: {
required: true,
maxLength: 40,
},
bio: {
maxLength: 300,
},
terms: {
required: true,
},
},
},
]);
NFSFU234FormValidation.autoInit();
const validator = new NFSFU234FormValidation({
form: "signupForm",
});
function showStep(step) {
document.querySelectorAll("section").forEach((section) => {
section.hidden = true;
});
document.getElementById(`step-${step}`).hidden = false;
}
async function validateStep(fields) {
for (const field of fields) {
const input = document.querySelector(`[name="${field}"]`);
const result = await validator.validateInput(input);
if (result !== true) {
return false;
}
}
return true;
}
document
.getElementById("step1Next")
.addEventListener("click", async () => {
if (await validateStep(["email", "password"])) {
showStep(2);
}
});
document
.getElementById("step2Back")
.addEventListener("click", () => {
showStep(1);
});
document
.getElementById("step2Next")
.addEventListener("click", async () => {
if (await validateStep(["displayName", "bio"])) {
showStep(3);
}
});
document
.getElementById("step3Back")
.addEventListener("click", () => {
showStep(2);
});
document
.getElementById("signupForm")
.addEventListener("submit", async (e) => {
e.preventDefault();
const result = await validator.submit();
if (result.type === "success") {
console.log("Registration complete.");
}
});
Why this approach?
Validating one step at a time creates a much better user experience than validating the whole form after the user clicks Create account.
Each section is completed before moving forward, while the final .submit() still performs a complete validation of the form before submission.
Perfect for
- Registration wizards
- Checkout flows
- Employee onboarding
- Loan applications
- Surveys
- Long profile forms