Thomas Kim
Modern Form Validation Techniques
Form validation is crucial for data integrity and user experience. Let’s explore modern approaches to building robust, user-friendly forms.
Client-Side Validation Benefits
- Immediate feedback without server round-trips
- Better user experience
- Reduced server load
- Still need server-side validation for security!
Form Validation Example
Here’s a complete form validation implementation:
import { useState } from 'react'
function ValidationForm() {
const [formData, setFormData] = useState({
email: '',
password: '',
confirmPassword: '',
username: ''
})
const [errors, setErrors] = useState({})
const [touched, setTouched] = useState({})
const validateEmail = (email) => {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return re.test(email)
}
const validateField = (name, value) => {
switch(name) {
case 'email':
return validateEmail(value) ? '' : 'Please enter a valid email'
case 'password':
return value.length >= 8 ? '' : 'Password must be at least 8 characters'
case 'confirmPassword':
return value === formData.password ? '' : 'Passwords do not match'
case 'username':
return value.length >= 3 ? '' : 'Username must be at least 3 characters'
default:
return ''
}
}
const handleChange = (e) => {
const { name, value } = e.target
setFormData(prev => ({ ...prev, [name]: value }))
if (touched[name]) {
setErrors(prev => ({
...prev,
[name]: validateField(name, value)
}))
}
}
const handleBlur = (e) => {
const { name, value } = e.target
setTouched(prev => ({ ...prev, [name]: true }))
setErrors(prev => ({
...prev,
[name]: validateField(name, value)
}))
}
const handleSubmit = (e) => {
e.preventDefault()
const newErrors = {}
Object.keys(formData).forEach(key => {
const error = validateField(key, formData[key])
if (error) newErrors[key] = error
})
if (Object.keys(newErrors).length === 0) {
console.log('Form submitted successfully!', formData)
} else {
setErrors(newErrors)
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Username</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.username && touched.username && (
<span className="error">{errors.username}</span>
)}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email && (
<span className="error">{errors.email}</span>
)}
</div>
<button type="submit">Submit</button>
</form>
)
}Validation Patterns
Email Validation
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return regex.test(email)
}Password Strength
function validatePassword(password) {
const hasLength = password.length >= 8
const hasUpperCase = /[A-Z]/.test(password)
const hasLowerCase = /[a-z]/.test(password)
const hasNumber = /\d/.test(password)
const hasSpecialChar = /[!@#$%^&*]/.test(password)
return {
isValid: hasLength && hasUpperCase && hasLowerCase && hasNumber,
strength: [hasLength, hasUpperCase, hasLowerCase, hasNumber, hasSpecialChar]
.filter(Boolean).length
}
}Form Libraries
For complex forms, consider these libraries:
React Hook Form
import { useForm } from 'react-hook-form'
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm()
const onSubmit = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Invalid email address'
}
})}
/>
{errors.email && <span>{errors.email.message}</span>}
<button type="submit">Submit</button>
</form>
)
}Formik + Yup
import { Formik, Form, Field } from 'formik'
import * as Yup from 'yup'
const validationSchema = Yup.object({
email: Yup.string()
.email('Invalid email')
.required('Required'),
password: Yup.string()
.min(8, 'Must be at least 8 characters')
.required('Required')
})
function MyForm() {
return (
<Formik
initialValues={{ email: '', password: '' }}
validationSchema={validationSchema}
onSubmit={(values) => console.log(values)}
>
{({ errors, touched }) => (
<Form>
<Field name="email" type="email" />
{errors.email && touched.email && <div>{errors.email}</div>}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}Best Practices
- Validate on blur - Don’t annoy users with errors while typing
- Show success states - Positive feedback improves UX
- Clear error messages - Tell users exactly what’s wrong
- Disable submit on invalid - Prevent unnecessary submissions
- Always validate server-side - Never trust client-side validation alone
Conclusion
Good form validation enhances user experience and data quality. Start with native HTML5 validation, then enhance with JavaScript for complex scenarios!
Related
Jan 28, 2025