I remember the first time I built a form in React. It looked simple - just two fields, name and email. But the moment I tried adding validation, it turned into a mess of if statements, onBlur
handlers, and spaghetti code that made me question my life choices. That’s when I discovered libraries like Yup
, Zod
, and Joi
. Suddenly, form validation wasn’t just easier - it was clean, declarative, and actually enjoyable. In this post, I’ll walk you through each of these tools using the same simple form example, so you can decide which one fits your style (and sanity) best.
Form validation in React is essential to ensure correct data and avoid headaches on the backend. In this article, we’ll show you three popular ways to validate forms:
We’ll use the same simple example for all three: a form with name (required) and email (required and valid).
Base Setup (Used for All)
npm install react-hook-form
Base form (no validation yet)
import { useForm } from 'react-hook-form';
export default function ContactForm({ resolver }) {
const { register, handleSubmit, formState: { errors } } = useForm({ resolver });
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Name:</label>
<input {...register('name')} />
<p>{errors.name?.message}</p>
</div>
<div>
<label>Email:</label>
<input {...register('email')} />
<p>{errors.email?.message}</p>
</div>
<button type="submit">Submit</button>
</form>
);
}
1. Yup
npm install yup @hookform/resolvers
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import ContactForm from './ContactForm';
const schema = yup.object({
name: yup.string().required('Name is required'),
email: yup.string().email('Invalid email').required('Email is required'),
});
export default function YupForm() {
return <ContactForm resolver={yupResolver(schema)} />;
}
✔️ Pros:
- Intuitive syntax
- Mature and widely used
⚠️ Cons:
- Weaker TypeScript support compared to Zod
2. Zod
npm install zod @hookform/resolvers
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import ContactForm from './ContactForm';
const schema = z.object({
name: z.string().nonempty('Name is required'),
email: z.string().email('Invalid email'),
});
export default function ZodForm() {
return <ContactForm resolver={zodResolver(schema)} />;
}
✔️ Pros:
- Great TypeScript support (auto-inferred types)
- Modern and lightweight
⚠️ Cons:
- Smaller community than Yup (but growing fast)
3. Joi (with React Hook Form)
npm install joi @hookform/resolvers
import Joi from 'joi';
import { joiResolver } from '@hookform/resolvers/joi';
import ContactForm from './ContactForm';
const schema = Joi.object({
name: Joi.string().required().messages({ 'string.empty': 'Name is required' }),
email: Joi.string().email({ tlds: { allow: false } }).required().messages({
'string.email': 'Invalid email',
'string.empty': 'Email is required',
}),
});
export default function JoiForm() {
return <ContactForm resolver={joiResolver(schema)} />;
}
✔️ Pros:
- Popular in backend validation (Node.js/Hapi)
- Flexible and powerful
⚠️ Cons:
- Less intuitive for front-end
- Not as React-centric
Conclusion
Library | Easy to Use | TS Support | Maturity | Best For... |
---|---|---|---|---|
Yup | ✅ Yes | 🟡 Medium | ✅ High | Existing or mature projects |
Zod | ✅ Yes | ✅ Excellent | 🟡 Medium | New projects with TypeScript |
Joi | 🟡 Okay | 🟡 Medium | ✅ High | Backend-heavy environments |
💬 Pro Tip: If you're using React Hook Form, go with Zod for new TypeScript projects. But Yup is still a solid and reliable choice.
If you liked this, 💙 leave a like or share your favorite validation library in the comments!
Top comments (4)
Nice post.
thanks so much
Very useful
Thanku