Forms are an essential part of most web applications. The forms are used for registration, checkout, onboarding, profile creation and many other tasks. When these forms contain many fields, it will become hard to display on a single page and can reduce the user experience. A multi-step form solves this problem by providing a better way to organize the form fields. It divides the larger forms into smaller sections. This helps the user to complete one section before moving to the next section. Users can also return to the previous step to update their information if they want. In this blog, we will build a simple multi-step form using react and useState.
1. Create a React Project
First let's create a new react project.
npm create vite@latest react-multi-step-form -template react
cd react-multi-step-form
npm install
npm run dev
2. Manage the Form State
Before building the form, we also need to decide on how to manage the data of the form. A multi-step form is required to need a minimum of two states. A state to store the current step of the form and a state to store the values entered by the user.
Create a file named MultiStepForm.jsx and add this:
import { useState } from "react";
export default function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
firstName: "",
lastName: "",
email: "",
phone: "",
});
return <div></div>;3. Create a Function to Update Form Data
Next, we need a function to update the form data. Each input value needs to be saved in the state. Add this function to manage saving the data on each change.
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((current) => ({
...current,
[name]: value,
}));
};4. Create the Form Steps
Now, we can create the form steps. In this tutorial, we are creating mainly three steps or sections for our multi-step form. The steps are personal information, contact information, and the review step. Update the component with this code.
{step === 1 && (
<div>
<h2>Personal Information</h2>
<input
name="firstName"
value={formData.firstName}
onChange={handleChange}
placeholder="First name"
/>
<input
name="lastName"
value={formData.lastName}
onChange={handleChange}
placeholder="Last name"
/>
</div>
)}
{step === 2 && (
<div>
<h2>Contact Information</h2>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email address"
/>
<input
name="phone"
value={formData.phone}
onChange={handleChange}
placeholder="Phone number"
/>
</div>
)}
{step === 3 && (
<div>
<h2>Review Your Information</h2>
<p>Name: {formData.firstName} {formData.lastName}</p>
<p>Email: {formData.email}</p>
<p>Phone: {formData.phone}</p>
</div>
)}5. Add Next and Previous Buttons
Now, we create the buttons for navigation. We are creating two buttons for next and previous navigations. Update our component and add these functions.
const nextStep = () => {
setStep((current) => current + 1);
};
const previousStep = () => {
setStep((current) => current - 1);
};Add these buttons below the form section for the steps.
<div>
{step > 1 && (
<button onClick={previousStep}>
Previous
</button>
)}
{step < 3 ? (
<button onClick={nextStep}>
Next
</button>
) : (
<button onClick={handleSubmit}>
Submit
</button>
)}
</div>
6. Add Form Validation
Next, we can look at validation of the form data. Validation plays a crucial role in validating the imputed data before submitting to the backend or storing it. This helps in finding the missing data and wrong data.
Add a validation function to the component.
const validateStep = () => {
if (step === 1) {
return formData.firstName.trim() && formData.lastName.trim();
}
if (step === 2) {
return (
formData.email.trim() &&
formData.phone.trim()
);
}
return true;
};Now, update the nextStep function with this.
const nextStep = () => {
if (!validateStep()) {
alert("Please fill in all required fields.");
return;
}
setStep((current) => current + 1);
};7. Add a Progress Indicator
A progress indicator helps the user to get an idea of where they are in the progress of the form. It shows the form's progress and how many steps remain to complete. Update the component with this code.
<div className="mb-6">
<p className="mb-2 text-sm text-gray-600">
Step {step} of 3
</p>
<div className="h-2 rounded-full bg-gray-200">
<div
className="h-2 rounded-full bg-blue-600"
style={{ width: `${(step / 3) * 100}%` }}
/>
</div>
</div>
8. Handle Form Submission
After the user completes the form, it needs to be submitted. So, we can create a function that will handle the form submission.
const handleSubmit = async () => {
const response = await fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
if (response.ok) {
alert("Form submitted successfully!");
}
};9. Complete Multi-Step Form Component
import { useState } from "react";
export default function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
firstName: "",
lastName: "",
email: "",
phone: "",
});
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((current) => ({
...current,
[name]: value,
}));
};
const validateStep = () => {
if (step === 1) {
return (
formData.firstName.trim() &&
formData.lastName.trim()
);
}
if (step === 2) {
return (
formData.email.trim() &&
formData.phone.trim()
);
}
return true;
};
const nextStep = () => {
if (!validateStep()) {
alert("Please fill in all required fields.");
return;
}
setStep((current) => current + 1);
};
const previousStep = () => {
setStep((current) => current - 1);
};
const handleSubmit = () => {
console.log("Form Data:", formData);
alert("Form submitted successfully!");
};
return (
<div className="mx-auto max-w-lg rounded-lg border bg-white p-6">
<div className="mb-6">
<p className="mb-2 text-sm text-gray-600">
Step {step} of 3
</p>
<div className="h-2 rounded-full bg-gray-200">
<div
className="h-2 rounded-full bg-blue-600"
style={{ width: `${(step / 3) * 100}%` }}
/>
</div>
</div>
{step === 1 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold">
Personal Information
</h2>
<input
name="firstName"
value={formData.firstName}
onChange={handleChange}
placeholder="First name"
className="w-full rounded border p-2"
/>
<input
name="lastName"
value={formData.lastName}
onChange={handleChange}
placeholder="Last name"
className="w-full rounded border p-2"
/>
</div>
)}
{step === 2 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold">
Contact Information
</h2>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email address"
className="w-full rounded border p-2"
/>
<input
name="phone"
value={formData.phone}
onChange={handleChange}
placeholder="Phone number"
className="w-full rounded border p-2"
/>
</div>
)}
{step === 3 && (
<div className="space-y-3">
<h2 className="text-lg font-semibold">
Review Your Information
</h2>
<p>
Name: {formData.firstName} {formData.lastName}
</p>
<p>Email: {formData.email}</p>
<p>Phone: {formData.phone}</p>
</div>
)}
<div className="mt-6 flex justify-between">
{step > 1 ? (
<button
onClick={previousStep}
className="rounded border px-4 py-2"
>
Previous
</button>
) : (
<span />
)}
{step < 3 ? (
<button
onClick={nextStep}
className="rounded bg-blue-600 px-4 py-2 text-white"
>
Next
</button>
) : (
<button
onClick={handleSubmit}
className="rounded bg-green-600 px-4 py-2 text-white"
>
Submit
</button>
)}
</div>
</div>
);
}A multi-step form helps in organizing and arranging our large forms into smaller sections. It will help users complete it more easily. In this blog, we have checked how to create a simple multi-step form using React and useState. We have also checked form validation and form submission.
To read more about How to Lazy Load Components in React, refer to our blog How to Lazy Load Components in React.