Validate multiple fields using yup.
Written by Thomas Duffy
Intro
I started using yup for some basic form/data validation, and so far, it has been an excellent experience. Recently, I ran into an issue when validating multiple input fields. Yup would validate each field until it ran into a failure, leaving the fields below it unvalidated. Hopefully, this post will help anyone struggling with getting yup to validate all the data fields before throwing the error. Let’s get started.
Problem
Below is an image of the validation issue I’m describing above.
Code for the validation above.
function validateForm(data) {
return object()
.shape({
first_name: string().required(),
last_name: string().required(),
web_address: string().required(),
email: string()
.email()
.required(),
})
.validate(data);
}
While this may be useful for some scenarios, it creates a weird user experience. Not having all the errors surfaced at once forces the user to correct each validation error one at a time, which in this use case is not ideal.
Intended behavior
The intended behavior I was looking for is to have yup continue validating the form or data fields even if there are validation failures. Below is an example of what I’m talking about.
Solution
So there are a couple of different ways to validate data in Yup. Below, I’m using the validate
method, which throws a validation error that looks like this.
ValidationError = {
name: 'ValidationError',
path: 'first_name',
errors: ['first_name is a required field' ]
inner: []
}
This error above is what you use to show the user what field needs to be corrected in your form. The path property will match the scheme property you passed in.
The validate
method takes in two parameters. validate(value, options)
. The value
is the data you’re passing in to validate against the schema
you defined.
The options
param has the following options.
Options = {
strict: boolean = false;
abortEarly: boolean = true;
stripUnknown: boolean = false;
recursive: boolean = true;
context?: object;
}
Notice the abortEarly
property is defaulted to true
. This is where the problem is. If you don’t pass an options object into validate
, Yup will abort out of the validation chain, leaving your form/data partially validated. So the fix is simple, pass in an options object like the one below.
validate(data, { abortEarly: false });
So a full example would like this.
function validateForm(data) {
return object()
.shape({
first_name: string().required(),
last_name: string().required(),
web_address: string().required(),
email: string()
.email()
.required(),
})
.validate(data, { abortEarly: false });
}
Now, if you look at the ValidationError
, you’ll see this.
ValidationError = {
name: 'ValidationError',
errors: ['first_name is a required field', 'last_name is a required field'],
inner: [ValidationError, ValidationError],
};
Notice that the inner
property and error
property have all the errors from the form. You can now iterate over them and let the user know what fields need to be validated on the first submission! If you would like to see a complete working example of the solution above, you can take a look here.
Conclusion
So that is how you set up yup to validate your whole form instead of one field at a time. If you liked this post, please sign up for my newsletter to get notified of when I post some more articles!