Last time out, I showed how we can build a React form using the form library called Formik. In this article, I will be showing you how to validate the form we created using Yup.
Below is how the form and code look like at the end of the first issue of this article:
const UserForm = () => {
return (
<Formik
initialValues={{
firstname: ''
lastname: ''
email: ''
country: ''
state: ''
zip: ''
}}
onSubmit={() => {
console.log('form submitted')
}}
>
{ ({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
}) => (
<div className="container">
<div className="col-md-12 mt-5">
<form onSubmit={handleSubmit}>
<h4 className="mb-3">Personal information</h4>
<div className="row">
<div className="col-md-6 mb-3">
<label htmlFor="firstname">First name</label>
<input
type="text"
className="form-control"
id="firstname"
name="firstname"
value={values.firstname}
/>
</div>
<div className="col-md-6 mb-3">
<label htmlFor="lastname">Last name</label>
<input
type="text"
className="form-control"
id="lastname"
name="lastname"
value={values.lastname}
/>
</div>
</div>
<div className="mb-3">
<label htmlFor="email">Email</label>
<input
type="email"
className="form-control"
id="email"
name="email"
placeholder="you@example.com"
value={values.email}
/>
</div>
<div className="row">
<div className="col-md-5 mb-3">
<label htmlFor="country">Country</label>
<select
className="custom-select d-block w-100"
id="country"
name="country"
value={values.country}
>
<option value="">Choose...</option>
<option value="NIG">Nigeria</option>
<option value="GH">Ghana</option>
<option value="SA">South Africa</option>
</select>
</div>
<div className="col-md-4 mb-3">
<label htmlFor="state">State</label>
<select
className="custom-select d-block w-100"
id="state"
name="state"
value={values.state}
>
<option value="">Choose...</option>
<option value="lagos">Lagos</option>
<option value="east legion">East Legion</option>
<option value="cape town">Cape Town</option>
</select>
</div>
<div className="col-md-3 mb-3">
<label htmlFor="zip">Zip</label>
<input
type="text"
className="form-control"
id="zip"
name="zip"
value={values.zip}
/>
</div>
</div>
<hr className="mb-4"/>
<button className="btn btn-primary btn-lg btn-block" type="submit">
Submit
</button>
</form>
</div>
</div>
) }
</Formik>
)
}
If you are just coming across this article and you haven't read the first issue where I created the form using Formik, then you should probably go check out Building React Forms Painlessly With Formik
Now there are different ways we can validate our forms, we can validate the form manually with Formik or we can validate it using Yup. In this article, I will only be showing you the Yup way, because I have resolved to make this particular article less lengthy than the last one.
To begin we will have to install Yup by running npm install yup
in our command line. Make sure the current directory you are in when running this command is your React project folder.
In the previous article, I named the file that houses the form as userForm.js
. It is in this file we will be importing Yup as follows:
import * as Yup from 'yup'
Importing Yup into our file gives us access to a property called validationSchema
that we can add to the Formik
component
validationSchema = {Yup.object({
firstname: Yup
.string()
.required('Sorry, this is required')
.max(5, 'Sorry, name is too long'),
lastname: Yup
.string()
.required('Sorry, this is required'),
email: Yup
.string()
.required('Sorry, this is required')
.email('Invalid email format')
})}
We see from the code above that we can target specific properties that we want to validate in the validationSchema
, which in turn gives us access to several methods that we can use for validation. The strings provided in some of the methods are the error messages that will be displayed if a particular input field fails a validation.
Now we will need to add some logic to show the error message on our form
component when an input field fails a validation.
<div className="container">
<div className="col-md-12 mt-5">
<form onSubmit={handleSubmit}>
<h4 className="mb-3">Personal information</h4>
<div className="row">
<div className="col-md-6 mb-3">
<label htmlFor="firstname">First name</label>
<input
type="text"
className="form-control"
id="firstname"
name="firstname"
value={values.firstname}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.firstname && touched.firstname ?
<span style={{color: 'red'}}>
{errors.firstname}
</span>
: null}
</div>
<div className="col-md-6 mb-3">
<label htmlFor="lastname">Last name</label>
<input
type="text"
className="form-control"
id="lastname"
name="lastname"
value={values.lastname}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.lastname && touched.lastname ?
<span style={{color: 'red'}}>
{errors.lastname}
</span>
: null}
</div>
</div>
<div className="mb-3">
<label htmlFor="email">Email</label>
<input
type="email"
className="form-control"
id="email"
name="email"
placeholder="you@example.com"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email ?
<span style={{color: 'red'}}>
{errors.firstname}
</span>
: null}
</div>
<div className="row">
<div className="col-md-5 mb-3">
<label htmlFor="country">Country</label>
<select
className="custom-select d-block w-100"
id="country"
name="country"
value={values.country}
onChange={handleChange}
>
<option value="">Choose...</option>
<option value="NIG">Nigeria</option>
<option value="GH">Ghana</option>
<option value="SA">South Africa</option>
</select>
</div>
<div className="col-md-4 mb-3">
<label htmlFor="state">State</label>
<select
className="custom-select d-block w-100"
id="state"
name="state"
value={values.state}
onChange={handleChange}
>
<option value="">Choose...</option>
<option value="lagos">Lagos</option>
<option value="east legion">East Legion</option>
<option value="cape town">Cape Town</option>
</select>
</div>
<div className="col-md-3 mb-3">
<label htmlFor="zip">Zip</label>
<input
type="text"
className="form-control"
id="zip"
name="zip"
value={values.zip}
onChange={handleChange}
/>
</div>
</div>
<hr className="mb-4"/>
<button className="btn btn-primary btn-lg btn-block" type="submit">
Submit
</button>
</form>
</div>
</div>
We get access to the error messages passed into the methods in the validationSchema
from the errors object. To display the error message, errors.firstname
checks if there was an error when validating the firstname input field and touched.firstname
checks if the firstname input field was accessed or clicked on by the user. If both conditions passes, we display an error below the input field, else, no error is displayed.
The final code when we pass the validationSchema
as a property to the Formik
looks like this:
const UserForm = () => {
return (
<Formik
initialValues={{
firstname: ''
lastname: ''
email: ''
country: ''
state: ''
zip: ''
}}
validationSchema = {Yup.object({
firstname: Yup
.string()
.required('Sorry, this is required')
.max(5, 'Sorry, name is too long'),
lastname: Yup
.string()
.required('Sorry, this is required'),
email: Yup
.string()
.required('Sorry, this is required')
.email('Invalid email format')
})}
onSubmit={() => {
console.log('form submitted')
}}
>
{ ({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
}) => (
<div className="container">
<div className="col-md-12 mt-5">
<form onSubmit={handleSubmit}>
<h4 className="mb-3">Personal information</h4>
<div className="row">
<div className="col-md-6 mb-3">
<label htmlFor="firstname">First name</label>
<input
type="text"
className="form-control"
id="firstname"
name="firstname"
value={values.firstname}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.firstname && touched.firstname ?
<span style={{color: 'red'}}>
{errors.firstname}
</span>
: null}
</div>
<div className="col-md-6 mb-3">
<label htmlFor="lastname">Last name</label>
<input
type="text"
className="form-control"
id="lastname"
name="lastname"
value={values.lastname}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.lastname && touched.lastname ?
<span style={{color: 'red'}}>
{errors.lastname}
</span>
: null}
</div>
</div>
<div className="mb-3">
<label htmlFor="email">Email</label>
<input
type="email"
className="form-control"
id="email"
name="email"
placeholder="you@example.com"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email ?
<span style={{color: 'red'}}>
{errors.email}
</span>
: null}
</div>
<div className="row">
<div className="col-md-5 mb-3">
<label htmlFor="country">Country</label>
<select
className="custom-select d-block w-100"
id="country"
name="country"
value={values.country}
onChange={handleChange}
>
<option value="">Choose...</option>
<option value="NIG">Nigeria</option>
<option value="GH">Ghana</option>
<option value="SA">South Africa</option>
</select>
</div>
<div className="col-md-4 mb-3">
<label htmlFor="state">State</label>
<select
className="custom-select d-block w-100"
id="state"
name="state"
value={values.state}
onChange={handleChange}
>
<option value="">Choose...</option>
<option value="lagos">Lagos</option>
<option value="east legion">East Legion</option>
<option value="cape town">Cape Town</option>
</select>
</div>
<div className="col-md-3 mb-3">
<label htmlFor="zip">Zip</label>
<input
type="text"
className="form-control"
id="zip"
name="zip"
value={values.zip}
onChange={handleChange}
/>
</div>
</div>
<hr className="mb-4"/>
<button className="btn btn-primary btn-lg btn-block" type="submit">
Submit
</button>
</form>
</div>
</div>
) }
</Formik>
)
}
That is it for this article. As you can see, with Formik and Yup we are able to avoid unnecessary boilerplate code. Formik handles the validation by default. So as you are entering values and clicking submit, it is running the validation and won't submit until all form values can pass.
Fomik makes it easy to access and update form values. Use handleChange
to handle update and the values
object holds all the current values. Same with the errors
object we use to display the error messages for individual fields.
Above, is a demo on how our form should look and work.
Hope you find this article as useful as the first. Thanks and have a nice read.
Top comments (2)
Nice article..little thing i noticed..ur error display logic is supposed to look something like this
errors.email || touched.email
Nice article but it would be nice if you make the select options required.