Build a multi-step form with progress indicators, field validation at each step, and a single submission at the end. Improve completion rates for long forms.
Wrap each group of fields in a container (a div with a data-step attribute). Only one step is visible at a time. This approach keeps all fields in a single form element so the data is submitted together at the end.
<form id="multiStepForm" action="https://formslist.com/f/YOUR_FORM_HASH" method="POST">
<!-- Step 1: Personal Info -->
<div class="form-step" data-step="1">
<h3>Step 1: Personal Information</h3>
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<!-- Step 2: Details -->
<div class="form-step" data-step="2" style="display:none;">
<h3>Step 2: Project Details</h3>
<label for="company">Company</label>
<input type="text" id="company" name="company" />
<label for="budget">Budget Range</label>
<select id="budget" name="budget" required>
<option value="">Select...</option>
<option value="<5k">Under $5,000</option>
<option value="5k-15k">$5,000 - $15,000</option>
<option value="15k-50k">$15,000 - $50,000</option>
<option value="50k+">$50,000+</option>
</select>
</div>
<!-- Step 3: Message -->
<div class="form-step" data-step="3" style="display:none;">
<h3>Step 3: Your Message</h3>
<label for="message">Tell us about your project</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<!-- Progress & Navigation -->
<div class="form-nav">
<div class="progress">Step <span id="currentStep">1</span> of 3</div>
<button type="button" id="prevBtn" style="display:none;">Back</button>
<button type="button" id="nextBtn">Next</button>
<button type="submit" id="submitBtn" style="display:none;">Submit</button>
</div>
</form>Write JavaScript to handle moving between steps. The Next button validates the current step's fields before advancing. The Back button returns to the previous step without losing data. On the final step, the Next button is replaced with a Submit button.
<script>
const form = document.getElementById("multiStepForm");
const steps = form.querySelectorAll(".form-step");
const prevBtn = document.getElementById("prevBtn");
const nextBtn = document.getElementById("nextBtn");
const submitBtn = document.getElementById("submitBtn");
const currentStepEl = document.getElementById("currentStep");
let current = 0;
function showStep(index) {
steps.forEach((step, i) => {
step.style.display = i === index ? "block" : "none";
});
currentStepEl.textContent = index + 1;
prevBtn.style.display = index === 0 ? "none" : "inline-block";
nextBtn.style.display = index === steps.length - 1 ? "none" : "inline-block";
submitBtn.style.display = index === steps.length - 1 ? "inline-block" : "none";
}
function validateStep(index) {
const inputs = steps[index].querySelectorAll("input, select, textarea");
for (const input of inputs) {
if (!input.checkValidity()) {
input.reportValidity();
return false;
}
}
return true;
}
nextBtn.addEventListener("click", () => {
if (validateStep(current)) {
current++;
showStep(current);
}
});
prevBtn.addEventListener("click", () => {
current--;
showStep(current);
});
showStep(0);
</script>Add CSS to style the form steps, navigation buttons, and a progress bar. Visual progress indicators help users understand how many steps remain and increase form completion rates. Keep the design clean and consistent across all steps.
<style>
#multiStepForm {
max-width: 500px;
margin: 2rem auto;
font-family: sans-serif;
}
.form-step h3 {
margin-bottom: 1rem;
color: #1e293b;
}
label {
display: block;
margin-top: 0.75rem;
font-weight: 600;
font-size: 0.9rem;
}
input, select, textarea {
width: 100%;
padding: 0.5rem;
margin-top: 0.25rem;
border: 1px solid #d1d5db;
border-radius: 6px;
box-sizing: border-box;
}
.form-nav {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 1.5rem;
}
.progress {
flex: 1;
font-size: 0.85rem;
color: #6b7280;
}
button {
padding: 0.5rem 1.25rem;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
}
#prevBtn { background: #e5e7eb; color: #374151; }
#nextBtn, #submitBtn { background: #2563eb; color: white; }
#nextBtn:hover, #submitBtn:hover { background: #1d4ed8; }
</style>The form submits all fields from every step in a single POST request when the user clicks Submit on the final step. You can switch to an AJAX submission to stay on the page and show a success message instead of redirecting. FormsList receives all the data regardless of which step each field was on.
<script>
form.addEventListener("submit", async (e) => {
e.preventDefault();
if (!validateStep(current)) return;
submitBtn.disabled = true;
submitBtn.textContent = "Submitting...";
try {
const res = await fetch("https://formslist.com/f/YOUR_FORM_HASH", {
method: "POST",
body: new FormData(form),
});
if (res.ok) {
form.innerHTML = `
<div style="text-align:center;padding:2rem;">
<h3 style="color:#16a34a;">Thank you!</h3>
<p>Your submission has been received. We will be in touch soon.</p>
</div>`;
} else {
alert("Something went wrong. Please try again.");
submitBtn.disabled = false;
submitBtn.textContent = "Submit";
}
} catch {
alert("Network error. Please try again.");
submitBtn.disabled = false;
submitBtn.textContent = "Submit";
}
});
</script>Learn the best practices for validating form data on both the client and server side. Improve user experience, reduce errors, and keep your data clean.
Learn moreAdd a fully functional contact form to any HTML website in under 5 minutes. No JavaScript frameworks, no server setup — just plain HTML that works everywhere.
Learn moreBuild a fully functional contact form in React with hooks, validation, and email notifications. No backend server or API routes needed.
Learn moreSet up your form backend in under a minute. No server required, no complex configuration — just a simple endpoint for your forms.