Why Multi-Step Form Is Asked in Interviews
Multi-step forms are frequently asked in frontend machine coding rounds because they test structured thinking, state management, validation handling, and user flow design. Unlike simple forms, this problem requires managing data across multiple screens while maintaining a seamless experience.
It also reflects real-world use cases such as onboarding flows, checkout processes, loan applications, and profile creation systems.
What Problem Are We Solving?
Instead of showing a long and overwhelming form, multi-step forms break the input process into smaller steps. Each step collects a subset of data, improving user experience and completion rates.
Visualizing the User Experience
Imagine a signup flow where the user first enters personal details, then address information, and finally account credentials. The user clicks 'Next' to move forward and 'Back' to revisit previous steps. Progress is preserved across steps, and validation ensures correctness before proceeding.
Core Requirements
- Multiple steps with navigation (Next / Back)
- Shared form state across steps
- Validation per step
- Prevent navigation on invalid input
- Final submission with all data
- Optional progress indicator
Step 1: State Design
The key to solving this problem is managing a centralized state that persists across steps. Instead of separate states per step, a single object should hold all form data.
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: "",
email: "",
address: "",
city: "",
password: "",
});This ensures that moving between steps does not reset previously entered data.
Step 2: Handling Input Changes
A generic handler simplifies updates across all fields.
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};Step 3: Navigation Logic
Navigation between steps should be controlled and predictable.
const nextStep = () => setStep((prev) => prev + 1);
const prevStep = () => setStep((prev) => prev - 1);Step 4: Step-wise Validation
Each step should validate only its relevant fields before allowing the user to proceed.
const validateStep = () => {
if (step === 1) {
return formData.name && formData.email;
}
if (step === 2) {
return formData.address && formData.city;
}
if (step === 3) {
return formData.password.length >= 6;
}
return true;
};Step 5: Rendering Steps
Each step renders only its relevant inputs.
const renderStep = () => {
switch (step) {
case 1:
return (
<>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
</>
);
case 2:
return (
<>
<input name="address" value={formData.address} onChange={handleChange} placeholder="Address" />
<input name="city" value={formData.city} onChange={handleChange} placeholder="City" />
</>
);
case 3:
return (
<>
<input name="password" type="password" value={formData.password} onChange={handleChange} placeholder="Password" />
</>
);
default:
return null;
}
};Step 6: Complete Component
import React, { useState } from "react";
export default function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: "",
email: "",
address: "",
city: "",
password: "",
});
const handleChange = (e: any) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const validateStep = () => {
if (step === 1) return formData.name && formData.email;
if (step === 2) return formData.address && formData.city;
if (step === 3) return formData.password.length >= 6;
return true;
};
const nextStep = () => {
if (!validateStep()) {
alert("Please fill all fields correctly");
return;
}
setStep((prev) => prev + 1);
};
const prevStep = () => setStep((prev) => prev - 1);
const handleSubmit = () => {
if (!validateStep()) return;
console.log("Final Data:", formData);
};
const renderStep = () => {
switch (step) {
case 1:
return (
<>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
</>
);
case 2:
return (
<>
<input name="address" value={formData.address} onChange={handleChange} placeholder="Address" />
<input name="city" value={formData.city} onChange={handleChange} placeholder="City" />
</>
);
case 3:
return (
<>
<input name="password" type="password" value={formData.password} onChange={handleChange} placeholder="Password" />
</>
);
default:
return null;
}
};
return (
<div>
<h2>Step {step}</h2>
{renderStep()}
<div>
{step > 1 && <button onClick={prevStep}>Back</button>}
{step < 3 && <button onClick={nextStep}>Next</button>}
{step === 3 && <button onClick={handleSubmit}>Submit</button>}
</div>
</div>
);
}How the Flow Works
The user fills Step 1 and clicks Next. If validation passes, Step 2 appears. Data persists across steps. The user can go back and edit previous steps. Finally, all data is submitted together.
Common Mistakes
- Losing data when switching steps
- Not validating before moving forward
- Hardcoding step logic instead of scalable design
- Coupling UI and logic too tightly
Advanced Improvements
- Add progress bar
- Persist data in localStorage
- Use React Hook Form + Zod
- Add async validation (email check)
- Add stepper UI (like checkout)
Real-World Example
In an e-commerce checkout, Step 1 collects shipping info, Step 2 collects payment details, and Step 3 confirms the order. This structure improves user completion rate compared to a long single form.
Final Takeaway
Multi-step forms test how well you manage state, validation, and user flow. A strong implementation keeps logic clean, prevents invalid transitions, and ensures smooth navigation.
In machine coding rounds, the goal is not just to make it work but to design it like a production-ready component.