Building a Multi-Step Form with JavaScript :-A Beginner-Friendly Guide

Hey there! 👋
I recently worked on a super fun assignment from the Frontend Mentor website, a multi-step form project. This project was part of my Web2Bridge advanced class, and it really pushed me to level up my JavaScript skills. Trust me, it was quite the journey!
In this article, I’ll explain how I built this multi-step form, the challenges I faced, and the lessons I learned along the way. If you’re also learning JavaScript or working on a similar project, this might help you gain some clarity.
A multi-step form splits a long form into smaller sections, making it easier for users to fill out without feeling overwhelmed. Each section (or step) collects a bit of information, and the user can move back and forth between steps before submitting the final details.
In this project, the form lets users:
Choose a subscription plan
Switch between monthly and yearly pricing
Add optional services (add-ons)
See a final summary before confirming
Let’s start with the JavaScript code that powers this form!
Setting Up Our Variables
const steps = document.querySelectorAll(".stp");
const circleSteps = document.querySelectorAll(".step");
const formInputs = document.querySelectorAll(".step-1 form input");
const plans = document.querySelectorAll(".plan-card");
const switcher = document.querySelector(".switch");
const addons = document.querySelectorAll(".box");
const total = document.querySelector(".total b");
const planPrice = document.querySelector(".plan-price");
let time;
let currentStep = 1;
let currentCircle = 0;
const obj = { plan: null, kind: null, price: null };
Here I grab all the elements we’ll need like form steps, input fields, plan cards, and buttons using document.querySelectorAll()
and document.querySelector()
. We also set up some variables to keep track of the current step and selected plan.
Moving Between Steps
steps.forEach((step) => {
const nextBtn = step.querySelector(".next-stp");
const prevBtn = step.querySelector(".prev-stp");
if (prevBtn) {
prevBtn.addEventListener("click", () => {
document.querySelector(`.step-${currentStep}`).style.display = "none";
currentStep--;
document.querySelector(`.step-${currentStep}`).style.display = "flex";
circleSteps[currentCircle].classList.remove("active");
currentCircle--;
});
}
nextBtn.addEventListener("click", () => {
if (currentStep < 5 && validateForm()) {
document.querySelector(`.step-${currentStep}`).style.display = "none";
currentStep++;
currentCircle++;
setTotal();
document.querySelector(`.step-${currentStep}`).style.display = "flex";
circleSteps[currentCircle].classList.add("active");
summary(obj);
}
});
});
Here’s how it handle the “Next” and “Previous” buttons. When a button is clicked, the current step gets hidden, and the next or previous one is shown. I also update the step indicator (like the little circles at the top) to show progress.
Form Validation
function validateForm() {
let valid = true;
formInputs.forEach((input) => {
if (!input.value) {
valid = false;
input.classList.add("err");
findLabel(input).nextElementSibling.style.display = "flex";
} else {
input.classList.remove("err");
findLabel(input).nextElementSibling.style.display = "none";
}
});
return valid;
}
Before moving to the next step, this will check if the user has filled in all required fields. If not, it will show an error message.
Selecting a Plan
plans.forEach((plan) => {
plan.addEventListener("click", () => {
document.querySelector(".selected").classList.remove("selected");
plan.classList.add("selected");
obj.plan = plan.querySelector("b");
obj.price = plan.querySelector(".plan-priced");
});
});
When a user clicks on a plan, it highlight it and store the selected plan and price in the obj
object.
Switching Between Monthly and Yearly Plans
switcher.addEventListener("click", () => {
const val = switcher.querySelector("input").checked;
document.querySelector(".monthly").classList.toggle("sw-active", !val);
document.querySelector(".yearly").classList.toggle("sw-active", val);
switchPrice(val);
obj.kind = val;
});
The switcher lets users toggle between monthly and yearly pricing. It will update the plan pricing based on the selected option.
Showing Add-Ons
addons.forEach((addon) => {
addon.addEventListener("click", (e) => {
const addonSelect = addon.querySelector("input");
const ID = addon.getAttribute("data-id");
if (addonSelect.checked) {
addonSelect.checked = false;
addon.classList.remove("ad-selected");
showAddon(ID, false);
} else {
addonSelect.checked = true;
addon.classList.add("ad-selected");
showAddon(addon, true);
e.preventDefault();
}
});
});
This section lets users select optional add-ons for their plan.
Calculating the Total Price
function setTotal() {
const res = planPrice.innerHTML.replace(/\D/g, "");
const addonPrices = document.querySelectorAll(".selected-addon .servic-price");
let val = Array.from(addonPrices).reduce((sum, price) => sum + Number(price.innerHTML.replace(/\D/g, "")), 0);
total.innerHTML = `$${val + Number(res)}/${time ? "yr" : "mo"}`;
}
This grab the base plan price, add the prices of any selected add-ons, and update the total.
Challenges I Faced
Form validation: Ensuring each input field had proper error handling.
State management: Keeping track of user selections and syncing them across different steps.
Dynamic UI updates: Reflecting changes without refreshing the page.
Lessons Learned
JavaScript can get messy without proper structure, so planning helps.
DOM manipulation and event listeners are powerful, but you need to use them carefully.
Debugging is your best friend. Console logs saved me more times than I can count.
Wrapping Up
This project taught me much about JavaScript, especially interactivity and state management. If you’re working on a multi-step form, take your time and break the problem into smaller pieces.
I’d love to hear your thoughts! Have you built something similar? Let’s connect in the comments.
Thanks for reading! 😊
Subscribe to my newsletter
Read articles from Ayokomi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
