Multi-step forms are used to break a long form into smaller sections, improving user experience by displaying fewer inputs at a time. React provides a flexible way to create multi-step forms with state management. You can control the form’s state by maintaining it across multiple steps, allowing users to navigate between form sections seamlessly.
In this guide, we will cover how to implement multi-step forms in React using different state management approaches such as useState
, useReducer
, and libraries like Formik or React Hook Form.
1. Basic Multi-Step Form with useState
The most basic way to manage multi-step forms in React is using the useState
hook to track both the form data and the current step.
Example: Multi-Step Form Using useState
import React, { useState } from 'react';
const MultiStepForm = () => {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: '',
email: '',
address: '',
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevData) => ({
...prevData,
[name]: value,
}));
};
const handleNext = () => {
setStep(step + 1);
};
const handlePrev = () => {
setStep(step - 1);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted:', formData);
};
return (
<form onSubmit={handleSubmit}>
{step === 1 && (
<div>
<label>
Name:
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handleNext}>
Next
</button>
</div>
)}
{step === 2 && (
<div>
<label>
Email:
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handlePrev}>
Previous
</button>
<button type="button" onClick={handleNext}>
Next
</button>
</div>
)}
{step === 3 && (
<div>
<label>
Address:
<input
type="text"
name="address"
value={formData.address}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handlePrev}>
Previous
</button>
<button type="submit">Submit</button>
</div>
)}
</form>
);
};
export default MultiStepForm;
Explanation:
- Step Control: The
step
state variable is used to keep track of which step the user is on. - Form Data: The
formData
state holds the values of the form fields and updates as the user navigates through the steps. - Navigation: The “Next” and “Previous” buttons control the navigation between the steps.
- Form Submission: Once the user reaches the final step and clicks “Submit,” the form data is logged to the console.
2. Multi-Step Form with useReducer
useReducer
can be an alternative to useState
when the state logic becomes more complex, such as managing multiple steps in a form.
Example: Multi-Step Form Using useReducer
import React, { useReducer } from 'react';
const initialState = {
step: 1,
formData: {
name: '',
email: '',
address: '',
},
};
function reducer(state, action) {
switch (action.type) {
case 'SET_FIELD':
return {
...state,
formData: {
...state.formData,
[action.field]: action.value,
},
};
case 'NEXT_STEP':
return {
...state,
step: state.step + 1,
};
case 'PREV_STEP':
return {
...state,
step: state.step - 1,
};
default:
return state;
}
}
const MultiStepFormWithReducer = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const handleChange = (e) => {
dispatch({
type: 'SET_FIELD',
field: e.target.name,
value: e.target.value,
});
};
const handleNext = () => {
dispatch({ type: 'NEXT_STEP' });
};
const handlePrev = () => {
dispatch({ type: 'PREV_STEP' });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted:', state.formData);
};
return (
<form onSubmit={handleSubmit}>
{state.step === 1 && (
<div>
<label>
Name:
<input
type="text"
name="name"
value={state.formData.name}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handleNext}>
Next
</button>
</div>
)}
{state.step === 2 && (
<div>
<label>
Email:
<input
type="email"
name="email"
value={state.formData.email}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handlePrev}>
Previous
</button>
<button type="button" onClick={handleNext}>
Next
</button>
</div>
)}
{state.step === 3 && (
<div>
<label>
Address:
<input
type="text"
name="address"
value={state.formData.address}
onChange={handleChange}
/>
</label>
<button type="button" onClick={handlePrev}>
Previous
</button>
<button type="submit">Submit</button>
</div>
)}
</form>
);
};
export default MultiStepFormWithReducer;
Explanation:
- useReducer: The
useReducer
hook is used to handle both the form data and step transitions. Thereducer
function manages state updates, such as moving to the next step or setting the value of form fields. - Dispatch: The
dispatch
function sends actions to update the state, like changing the current step or updating form values.
3. Multi-Step Form with Formik
Formik simplifies form handling, especially for multi-step forms, as it handles validation, state management, and form submission. Here’s how to use Formik to build a multi-step form.
Example: Multi-Step Form with Formik
import React from 'react';
import { Formik, Form, Field } from 'formik';
const MultiStepFormikForm = () => {
return (
<Formik
initialValues={{ name: '', email: '', address: '' }}
onSubmit={(values) => {
console.log('Form submitted with values:', values);
}}
>
{({ values, setFieldValue }) => (
<Form>
<div>
<label>Name:</label>
<Field type="text" name="name" value={values.name} />
</div>
<div>
<label>Email:</label>
<Field type="email" name="email" value={values.email} />
</div>
<div>
<label>Address:</label>
<Field type="text" name="address" value={values.address} />
</div>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
};
export default MultiStepFormikForm;
Explanation:
- Formik: Formik handles all the form state management, so you don’t need to manually handle the input fields or form submission.
- Form: We can add a multi-step feature by showing specific steps based on user interaction and state changes.
4. Multi-Step Form with React Hook Form
React Hook Form is another library that is lightweight and optimized for performance. You can manage multi-step forms with React Hook Form by breaking the form into individual steps.
Example: Multi-Step Form with React Hook Form
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
const MultiStepRHFForm = () => {
const { control, handleSubmit, watch } = useForm({
defaultValues: {
name: '',
email: '',
address: '',
},
});
const onSubmit = (data) => {
console.log('Form submitted with data:', data);
};
const step = watch('step');
return (
<form onSubmit={handleSubmit(onSubmit)}>
{step === 1 && (
<div>
<label>Name:</label>
<Controller
name="name"
control={control}
render={({ field }) => <input {...field} />}
/>
<button type="button" onClick={() => setValue('step', 2)}>Next</button>
</div>
)}
{step === 2 && (
<div>
<label>Email:</label>
<Controller
name="email"
control={control}
render={({ field }) => <input {...field} />}
/>
<button type="button" onClick={() => setValue('step', 1)}>Previous</button>
<button type="button" onClick={() => setValue('step', 3)}>Next</button>
</div>
)}
{step === 3 && (
<div>
<label>Address:</label>
<Controller
name="address"
control={control}
render={({ field }) => <input {...field} />}
/>
<button type="button" onClick={() => setValue('step', 2)}>Previous</button>
<button type="submit">Submit</button>
</div>
)}
</form>
);
};
export default MultiStepRHFForm;
Explanation:
- useForm: The
useForm
hook from React Hook Form is used to handle form state management. - Controller: The
Controller
component is used to wrap input fields and register them with the form state. - Step Navigation: The
step
is tracked using thewatch
function, and users can navigate between steps.