Yup is a powerful JavaScript schema builder for value parsing and validation, commonly used with form libraries like Formik and React Hook Form. Here’s a comprehensive guide to using Yup effectively:
Basic Validation Schemas
1. Primitive Validations
import * as yup from 'yup';
// String validation
const nameSchema = yup.string().required('Name is required');
// Number validation
const ageSchema = yup.number()
.required('Age is required')
.positive('Must be positive')
.integer('Must be integer');
// Boolean validation
const acceptSchema = yup.boolean()
.oneOf([true], 'Must accept terms');
2. Object Schema (Common for Forms)
const userSchema = yup.object().shape({
firstName: yup.string().required('First name is required'),
lastName: yup.string().required('Last name is required'),
age: yup.number()
.required('Age is required')
.min(18, 'Must be at least 18')
.max(100, 'Must be less than 100'),
email: yup.string()
.email('Invalid email')
.required('Email is required')
});
Advanced Validation Techniques
1. Conditional Validation
const conditionalSchema = yup.object().shape({
isMember: yup.boolean(),
membershipId: yup.string().when('isMember', {
is: true,
then: yup.string().required('Membership ID is required')
})
});
2. Cross-field Validation
const passwordSchema = yup.object().shape({
password: yup.string()
.required('Password is required')
.min(8, 'Password must be at least 8 characters'),
confirmPassword: yup.string()
.oneOf([yup.ref('password'), null], 'Passwords must match')
});
3. Array Validation
const arraySchema = yup.object().shape({
tags: yup.array()
.of(yup.string().min(2, 'Tag too short'))
.min(1, 'At least one tag required')
.max(5, 'Maximum 5 tags allowed')
});
4. Nested Object Validation
const addressSchema = yup.object().shape({
address: yup.object().shape({
street: yup.string().required('Street is required'),
city: yup.string().required('City is required'),
zipCode: yup.string()
.matches(/^\d{5}$/, 'Must be 5 digit ZIP code')
})
});
Integration with Form Libraries
1. With Formik
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as yup from 'yup';
const validationSchema = yup.object().shape({
email: yup.string().email().required(),
password: yup.string().min(8).required()
});
function LoginForm() {
return (
<Formik
initialValues={{ email: '', password: '' }}
validationSchema={validationSchema}
onSubmit={values => console.log(values)}
>
<Form>
<Field name="email" type="email" />
<ErrorMessage name="email" component="div" />
<Field name="password" type="password" />
<ErrorMessage name="password" component="div" />
<button type="submit">Login</button>
</Form>
</Formik>
);
}
2. With React Hook Form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const schema = yup.object().shape({
username: yup.string().required(),
age: yup.number().positive().integer().required()
});
function ProfileForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(schema)
});
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<input {...register("username")} />
<p>{errors.username?.message}</p>
<input type="number" {...register("age")} />
<p>{errors.age?.message}</p>
<button type="submit">Save</button>
</form>
);
}
Custom Validation
1. Custom Validation Functions
const customSchema = yup.object().shape({
username: yup.string()
.test(
'username-available',
'Username is taken',
async value => {
const available = await checkUsernameAvailability(value);
return available;
}
)
});
2. Custom Error Messages
const messageSchema = yup.object().shape({
coupon: yup.string()
.required('Please enter a coupon code')
.test(
'valid-coupon',
'Invalid coupon code',
value => isValidCoupon(value)
)
});
Transformation and Parsing
1. Type Transformation
const transformSchema = yup.object().shape({
price: yup.string()
.transform(value => value.replace(/\$/g, ''))
.matches(/^\d+$/, 'Must be a number')
});
2. Default Values
const defaultSchema = yup.object().shape({
active: yup.boolean().default(true),
createdAt: yup.date().default(() => new Date())
});
Best Practices
- Reuse Schemas: Create reusable validation schemas for common fields
const emailSchema = yup.string().email().required();
- Compose Schemas: Combine smaller schemas into larger ones
const userSchema = yup.object().shape({
personalInfo: personalInfoSchema,
contactInfo: contactInfoSchema
});
- Localization: Customize error messages for different languages
yup.setLocale({
string: {
email: 'Invalid email format',
min: 'Must be at least ${min} characters'
}
});
- Async Validation: Handle server-side validation efficiently
.test('unique-email', 'Email already exists', async email => {
const exists = await checkEmailExists(email);
return !exists;
})
- Performance: For large forms, consider splitting validation schemas
Yup provides a declarative way to handle complex validation logic with excellent TypeScript support. Its chainable API makes it easy to create readable and maintainable validation rules for your forms.
