In React, form validation is an essential part of ensuring that the data submitted by the user is correct and adheres to the required format. While libraries like Formik or React Hook Form simplify the process, you can easily implement custom form validation without using any external libraries by leveraging React’s built-in features like useState
and useEffect
.
In this guide, we will walk through creating a simple form with custom validation in React using state management and handling validation logic manually.
Steps to Implement Custom Form Validation
- Create the Form Component
- Set up State for Form Fields
- Create Validation Functions
- Handle Form Submission
- Display Validation Error Messages
1. Create the Form Component
Let’s start by setting up a basic form that includes common fields like username
, email
, and password
.
import React, { useState } from 'react';
const CustomForm = () => {
// Step 2: Setting up state for form fields
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
});
const [errors, setErrors] = useState({
username: '',
email: '',
password: '',
});
const [isSubmitting, setIsSubmitting] = useState(false);
// Step 3: Create validation functions
const validate = () => {
let tempErrors = {};
let isValid = true;
if (!formData.username) {
tempErrors.username = 'Username is required';
isValid = false;
} else if (formData.username.length < 3) {
tempErrors.username = 'Username must be at least 3 characters';
isValid = false;
}
if (!formData.email) {
tempErrors.email = 'Email is required';
isValid = false;
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
tempErrors.email = 'Email is not valid';
isValid = false;
}
if (!formData.password) {
tempErrors.password = 'Password is required';
isValid = false;
} else if (formData.password.length < 6) {
tempErrors.password = 'Password must be at least 6 characters';
isValid = false;
}
setErrors(tempErrors);
return isValid;
};
// Step 4: Handle form submission
const handleSubmit = (event) => {
event.preventDefault();
// Check for validation
if (validate()) {
setIsSubmitting(true);
// Simulate form submission
setTimeout(() => {
alert('Form submitted successfully!');
setIsSubmitting(false);
}, 1000);
} else {
alert('Please fix the errors before submitting.');
}
};
// Handle input changes
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};
return (
<div>
<h1>Custom Form Validation</h1>
<form onSubmit={handleSubmit}>
<div>
<label>Username</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
{errors.username && <p style={{ color: 'red' }}>{errors.username}</p>}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}
</div>
<div>
<label>Password</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && <p style={{ color: 'red' }}>{errors.password}</p>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
</div>
);
};
export default CustomForm;
2. State Setup for Form Fields
We use the useState
hook to manage the form data. Here’s a breakdown:
formData
: Holds the current values for the form fields (username
,email
,password
).errors
: Holds error messages for each field.isSubmitting
: Tracks the submission state to disable the submit button while submitting.
3. Create Validation Functions
The validate
function checks each field for validity:
- It ensures that the
username
is not empty and has at least 3 characters. - It ensures that the
email
is not empty and is in the correct format. - It ensures that the
password
is not empty and has at least 6 characters.
If any of these conditions fail, we set the corresponding error message in the errors
state.
4. Handle Form Submission
The handleSubmit
function is triggered when the user submits the form. It prevents the default form submission and:
- Validates the form by calling
validate()
. - If the validation passes, it simulates a successful form submission (with
setTimeout
). - If there are validation errors, it alerts the user and prevents submission.
5. Display Validation Error Messages
If a field has an error, we display the error message beneath the input field. We conditionally render these messages based on whether the respective field in the errors
object contains a message.
6. Handling Input Changes
The handleChange
function updates the form field value as the user types in the input field. We also ensure that the respective field value in formData
is updated.
7. Disabling Submit Button While Submitting
To improve the user experience, we disable the submit button while the form is being submitted, indicated by the isSubmitting
state.
Full Example: Complete Form with Custom Validation
import React, { useState } from 'react';
const CustomForm = () => {
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
});
const [errors, setErrors] = useState({
username: '',
email: '',
password: '',
});
const [isSubmitting, setIsSubmitting] = useState(false);
const validate = () => {
let tempErrors = {};
let isValid = true;
if (!formData.username) {
tempErrors.username = 'Username is required';
isValid = false;
} else if (formData.username.length < 3) {
tempErrors.username = 'Username must be at least 3 characters';
isValid = false;
}
if (!formData.email) {
tempErrors.email = 'Email is required';
isValid = false;
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
tempErrors.email = 'Email is not valid';
isValid = false;
}
if (!formData.password) {
tempErrors.password = 'Password is required';
isValid = false;
} else if (formData.password.length < 6) {
tempErrors.password = 'Password must be at least 6 characters';
isValid = false;
}
setErrors(tempErrors);
return isValid;
};
const handleSubmit = (event) => {
event.preventDefault();
if (validate()) {
setIsSubmitting(true);
setTimeout(() => {
alert('Form submitted successfully!');
setIsSubmitting(false);
}, 1000);
} else {
alert('Please fix the errors before submitting.');
}
};
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};
return (
<div>
<h1>Custom Form Validation</h1>
<form onSubmit={handleSubmit}>
<div>
<label>Username</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
{errors.username && <p style={{ color: 'red' }}>{errors.username}</p>}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}
</div>
<div>
<label>Password</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && <p style={{ color: 'red' }}>{errors.password}</p>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
</div>
);
};
export default CustomForm;