DEV Community

Cover image for React Form Validation With Formik and Yup
J0shcodes
J0shcodes

Posted on

React Form Validation With Formik and Yup

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:

React form built with formik

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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')
})}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
teyim profile image
Teyim Asobo • Edited

Nice article..little thing i noticed..ur error display logic is supposed to look something like this
errors.email || touched.email

Collapse
 
dchief profile image
Emmanuel Kelechi Igwesi

Nice article but it would be nice if you make the select options required.