Form Validation
Form Validation
Form validation is crucial for ensuring that the data users submit to your application is correct, complete, and secure. In React, you can validate forms in several ways.
1. HTML5 Native Validation
Before writing custom JavaScript validation, you should leverage the built-in validation attributes provided by HTML5. This is the simplest approach and requires no extra React code.
required: The field cannot be empty.minLength/maxLength: Restricts text length.min/max: Restricts number ranges.type="email"/type="url": Enforces basic formatting.pattern: Validates against a Regex string.
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
required
minLength={3}
maxLength={15}
pattern="[A-Za-z0-9]+" // Only letters and numbers
/>
<button type="submit">Submit</button>
</form>
Note: HTML5 validation relies on the browser’s default tooltip UI, which is difficult to style and varies across browsers.
2. Custom Validation with State
For complex rules (e.g., “Passwords must match”) or custom UI designs, you must implement validation in React state using Controlled Components.
You typically track validation errors in a separate state object.
import { useState } from 'react';
function CustomValidationForm() {
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// 1. Clear previous errors
setError('');
// 2. Run validation rules
if (password.length < 8) {
setError('Password must be at least 8 characters long.');
return; // Stop submission
}
if (password !== confirmPassword) {
setError('Passwords do not match.');
return; // Stop submission
}
// 3. If no errors, proceed with submission
console.log("Form submitted successfully!");
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Password:</label>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>Confirm Password:</label>
<input
type="password"
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
/>
</div>
{/* Conditionally render the error message */}
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Register</button>
</form>
);
}
3. Using Form Libraries (Industry Standard)
Writing custom validation state for every form is exhausting. In professional environments, developers almost always use a form library combined with a schema validation library like Zod or Yup.
React Hook Form + Zod is currently the industry standard.
// Concept Example (Requires installing react-hook-form and zod)
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
// 1. Define the validation schema rules
const schema = z.object({
username: z.string().min(3, { message: "Too short" }),
age: z.number().min(18, { message: "Must be an adult" }),
});
function ModernForm() {
// 2. Let the library handle state and validation
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema)
});
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("username")} />
{errors.username && <p>{errors.username.message}</p>}
<input type="number" {...register("age", { valueAsNumber: true })} />
{errors.age && <p>{errors.age.message}</p>}
<button type="submit">Submit</button>
</form>
);
}
Interview Questions
Q: When building complex forms, why do many developers use libraries like React Hook Form instead of native controlled inputs?
A: Native controlled inputs re-render the entire form component on every single keystroke, which can cripple performance on massive forms. React Hook Form uses uncontrolled inputs and refs under the hood to completely eliminate keystroke re-renders, vastly improving performance while providing a simple API for validation.