How to Validate Forms in React with Custom Hooks
Introduction
Form validation is a critical part of any web application. In React, building a reusable validation hook gives you type safety, real-time feedback, and clean separation of concerns.
Step 1: Define Validation Rules
Start by creating a TypeScript interface for your validation rules:
interface ValidationRule {
required?: boolean;
minLength?: number;
pattern?: RegExp;
message: string;
}
Each rule can combine multiple constraints. The message field provides user-friendly error text.
Step 2: Build the useForm Hook
The hook manages form state, validation, and submission:
export function useForm<T extends Record<string, string>>(
initialValues: T,
rules: Partial<Record<keyof T, ValidationRule[]>>,
) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
const validate = useCallback((name: keyof T, value: string) => {
const fieldRules = rules[name];
if (!fieldRules) return '';
for (const rule of fieldRules) {
if (rule.required && !value) return rule.message;
if (rule.minLength && value.length < rule.minLength) return rule.message;
if (rule.pattern && !rule.pattern.test(value)) return rule.message;
}
return '';
}, [rules]);
return { values, errors, handleChange, isValid };
}
Step 3: Integrate with Components
Wire the hook into your form component:
function LoginForm() {
const { values, errors, handleChange, isValid } = useForm(
{ email: '', password: '' },
{
email: [{ required: true, message: 'Email is required' }],
password: [{ minLength: 8, message: 'Min 8 characters' }],
},
);
return <form onSubmit={...}>...</form>;
}
Conclusion
This pattern gives you reusable, type-safe form validation that works across any React form. The hook is composable — you can extend it with async validation, debouncing, or nested form support.