Use Formik+Yup to create a multi-step React form part 2: validate with Yup and display errors
So the last article I explained how to create a simple form with Formik, in this article, I want to talk about how to validate with Yup and display errors on your form.
Custom Validation
In the last article’s code, you can see how we usually do validation:
const validate = values => {
const errors = {};
if (!values.firstName) {
errors.firstName = "Required";
} if (!values.lastName) {
errors.lastName = "Required";
}
return errors;
};
<Formik
initialValues={formData}
validate={validate}
>
// put a form here
</Formik>
The basic idea is that there is a validate
prop in Formik
. We can pass the validation method into it, which will take the values
that the user inputs and validate it with your custom methods. In the end, errors
will be returned and can be used to display errors
messages.
However, very often we need to use the same validation for different apps. For example, if we want to validate the user first name is presented in another app, we have to go to our last app and copy codes there or write it all manually again. That’s annoying and very error-prone! Here’s where Yup comes as a handy tool.
Yup Validation
Schema
Yup is very useful because it embeds a lot of common validation. We can install it first:
npm install yup
And then import it in the document that we need to use validation like so:
import * as Yup from 'yup';
After this, we can freely use everything like so:
Yup.<data type>.<built-in method>
For example, to validate a string to be valid email format, do this:
email: Yup.string.email()
This will validate the email
field to see if it is a valid email.
To validate a whole object, we can do this:
let schema = yup.object().shape({
firstName: yup.string().required(),
lastName: yup.string().required(),
}),
});
It means schema
is a method to validate a whole object, only when all the fields meeting all the requirements then the validation will pass.
ValidationSchema
Alright, so how to use this schema to help us validate? Actually, Formik likes Yup so much that it offers an easy way to use this schema
: pass it to the validationSchema
prop in <Formik>
like this:
<Formik
initialValues={formData}
onSubmit={handleSubmit}
validationSchema={schema}
>
//Put the form here
</Formik>
So right now the magic happens! Formik will use the SignupSchema
to validate all the fields in the form. And if there are any invalid items, the returned errors
object will contain the field name and a message like so:
name: "A name is required."
I custom this message by passing the message to require
method in: Yup.string.required(“A name is required.”)
. Just check the documentation to find out how to pass a custom validation method .
Display error messages
How to access the error messages
As described from last article, Formik
is just a react component. Therefore, props can be passed in and passed out of the component. If we want to access the errors, we can extract it from the props Formik
has to offer to its child component like this:
<Formik
initialValues={formData}
onSubmit={handleSubmit}
validationSchema={schema}
>
{({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.firstName}
name="firstName"
/>
{errors.firstName &&
<div>
{errors.firstName}
</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
As we can see, the child component to Formik
here is the <form>.
handleSubmit, handleChange, handleBlur, values, errors, touched
are all passed to the <form>
component to use. In this way, we can access errors
that is returned by the validation method.
Render error messages conditionally
Now everything looks much better! However, there’s still one problem:
errors.firstName &&
<div>
{errors.firstName}
</div>}
From this, we can tell, the condition for displaying the error message is that errors.firstName
exists. That makes sense in the case where we touched the firstName
field but didn’t write anything — the validation
would fail because it is required
. But, what if in the very beginning, when the form is firstly loaded?
It is just loaded, we haven’t touched it yet, so it is empty! And since the validation loads when the form is loaded, so if we console.log
the error now, we will see errors.firstName
is true, which leads to a red error showing in the form.
But that’s not what we want right? We want it only shows up when it is touched and there is an error related to that field.
To achieve this, we can do:
touched.firstName && errors.firstName ? <div>{errors.firstName} : null
In this case, it will only shows the error when we touch it AND when there’s an error.
Note
One last important thing is that: if you want to use the touched
prop, make sure you use the handleBlur
in each field input that you will use touched
like so:
{({ handleSubmit, handleChange, handleBlur, values, errors, touched }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur} // IMPORTANT!!
value={values.firstName}
name="firstName"
/>
{errors.firstName &&
<div>
{errors.firstName}
</div>}
<button type="submit">Submit</button>
</form>
)}
That’s how it knows which field has been touched.
END
Thanks for reading! Now a form with validation and errors displaying correctly. Next step, we will talk about how to implement a multi-step form. Stay in touch!
Resources:
Use Formik+Yup to create a multi-step React form part 1: build a simple form