React Form Validation Using Formik and Yup

Forms are a great way to help you capture your website visitors' details- their names, email addresses, preferences, comments, and feedback. As developers, it's our job to make sure that when users interact with the forms we've created, the data they send is the way we expect it to be.

Formik is a React and React Native library that helps you create forms in React without difficulty. You can pair Formik with validation libraries like Yup to make the process even simpler.

In this article, we will learn how to handle form validation in React using Formik and Yup. We will set up form validation using Yup and Formik’s custom components and understand how Yup works well with Formik in handling Form validation.

Prerequisites

Project Setup

In this section, you will set up your website using Create React App (CRA) and install some dependencies for the sake of the tutorial. If you already have a React app set up you can skip this part.

  • In your terminal, run the following command to create a new React App with CRA:
npx create-react-app practice-forms

We are calling the website practice-forms because it's the name we would like to give to our React web app, but you can change it to whatever you want.

  • Once the installation is done, navigate to the newly created directory:
cd react-forms
  • Then, install Tailwind CSS to add some styling to your website:
npm install -D tailwindcss postcss autoprefixer
  • To set up Tailwind CSS, edit the file tailwind.config.js with the following content:
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
  • Also, replace the content of src/index.css with the following:
@tailwind base;
@tailwind components;
@tailwind utilities;

Creating the Form with Formik

  • Let us proceed by using Formik to create a form. Start by installing Formik:
npm i formik
  • Replace the content of src/App.js with the following:
import { useFormik } from 'formik';

function App() {
    const country = ['United States', 'Japan', 'France', 'other'];
    //TODO create formik instance

    return (
        <div className="bg-blue-300 min-w-screen min-h-screen overflow-x-hidden">
        </div>
    );
}

export default App;

All you did here was create the component App which does nothing special at the moment.

Notice how we imported the useFormik hook at the beginning of the file. You will use this hook to create a Formik instance with all the states and helpers you'll need.

The useFormik hook accepts as a parameter an object of configurations. These configurations can be used to modify and shape your form as you need.

Furthermore, you will pass the following properties in the object:

  1. initialValues: includes the form fields and their initial values.

  2. validationSchema: A Yup schema to validate the fields. You will understand the need for this shortly.

  3. onSubmit: a function to execute when the form is submitted.

Replace the TODO in the App component with the following:

const formik = useFormik({
    initialValues: {
      name: '',
      email: '',
      countries: country[0],
      age: '',
    },
    onSubmit: function (values) {
      alert(`You are registered! Name: ${values.name}. Email: ${values.email}. countries: ${values.countries}. 
        Age: ${values.age}`);
    }
  })

In the code block above, we set the value of the property initialValues to an object. This object's keys are the names of the fields in the form. Their values are the initial values.

In the onSubmit function, we receive the values object as a parameter. Note that the onSubmit function is only executed once the form is validated. We don't need to perform any validation inside this function.

Now, we can use the formik variable to create a form, then link its fields to the fields we defined in useFormik, also we have to link the validation and the submit handler.

Some properties of formik include:

  1. handleSubmit: the submit function that should be called when the form is submitted. This is usually assigned to the onSubmit event handler of form elements.

  2. errors: An object that has the field names as properties and the value of each is the error message resulting from validating that field if there are any errors.

  3. touched: An object that has the field names as properties and the value is a boolean indicating whether the user has interacted with the field or not.

  4. values: An object that has the field names as properties and the value of each is the current value of that field. It's usually used to set the value property of input elements.

  5. handleChange: A function that should be used as the handler of the change event of input elements. It's passed as the value of the onChange prop of elements.

  6. handleBlur: A function that should be used as the handler of the blur event of input elements. It's passed as the value of the onBlur prop of elements.

  • Replace the return statement in App with the following:
return (
    <div className="bg-blue-300 min-w-screen min-h-screen overflow-x-hidden">
      <form onSubmit={formik.handleSubmit} className="max-w-lg mx-auto bg-white rounded shadow-lg mt-7 p-3">
      <h1 className='text-3xl mb-3 text-center'>Register</h1>
        <div className='mb-4'>
          <label for="name">Full Name</label>
          <input type="text" name="name" id="name" 
            className={`block w-full rounded border py-1 px-2 ${formik.touched.name && formik.errors.name ? 'border-red-400' : 'border-gray-300'}`}
            onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.name} />
          {formik.touched.name && formik.errors.name && (
            <span className='text-red-400'>{formik.errors.name}</span>
          )}
        </div>
        <div className='mb-4'>
          <label for="email">Email</label>
          <input type="email" name="email" id="email"
            className={`block w-full rounded border py-1 px-2 ${formik.touched.email && formik.errors.email ? 'border-red-400' : 'border-gray-300'}`}
            onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.email} />
          {formik.touched.email && formik.errors.email && (
            <span className='text-red-400'>{formik.errors.email}</span>
          )}
        </div>
        <div className='mb-4'>
          <label for="country">country</label>
          <select name="country" id="country"
            className={`block w-full rounded border py-1 px-2 ${formik.touched.country&& formik.errors.country? 'border-red-400' : 'border-gray-300'}`}
            onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.country} >
            {country.map((country, index) => (
              <option value={country} key={index}>{country}</option>
            ))}
          </select>
          {formik.touched.country&& formik.errors.country&& (
            <span className='text-red-400'>{formik.errors.country}</span>
          )}
        </div>
        <div className='mb-4'>
          <label for="age">Age</label>
          <input type="number" name="age" id="age"
            className={`block w-full rounded border py-1 px-2 ${formik.touched.age && formik.errors.age ? 'border-red-400' : 'border-gray-300'}`}
            onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.age} />
          {formik.touched.age && formik.errors.age && (
            <span className='text-red-400'>{formik.errors.age}</span>
          )}
        </div>
        <div className='text-center'>
          <button className='bg-blue-500 rounded p-3 text-white' type='submit'>Submit</button>
        </div>
      </form>
    </div>
  );

Notice how we used all the properties in the formik variable mentioned earlier. Here, we pass in the handleChange helper functions to the inputs’ onChange attribute.

Running the program

Voila! We've created the form and it can be viewed by running the command below on our terminal.

npm start

You can then open the website at localhost:3000 (default port). If you open the website, you will see the form with 4 fields.

Adding Validation with Yup

In this section, we will be taking you through how you can add validation to the form using Yup.

First, we need to install Yup. Run the following in your terminal:

npm i yup

Yup has a lot of methods and validation rules you can use. The way it works with Formik is dynamic, we need to create a validation schema and pass it to useFormik as a value to the property validationSchema.

Yup validation schemas are created using Yup.object method which takes as a parameter, an object. This object has the field names as properties and their values are validation rules from the Yup library.

  • Import Yup at the beginning of src/App.js:
import * as Yup from 'yup';
  • Then, let's add the property validationSchema to the object passed to useFormik with the following value:
const formik = useFormik({
    ...,
    validationSchema: Yup.object({
      name: Yup.string()
              .label('Full Name')
              .required(),
      email: Yup.string()
              .email()
              .required(),
      country: Yup.string()
                  .oneOf(countries, 'The country you chose does not exist'),
      age: Yup.number()
            .min(18, 'You need to be older than 18 to register')
            .required()
    })
  })

You add the following validation rules:

  1. name: Should be a string and we set it to a required field i.e. users must provide an answer for the field. We also used the label method to ensure that when the error message is shown it refers to the field as "Full Name". By default, the fields are referred to by the field name, which in this case is name.

  2. email: Should be a string, an email, and the field is required.

  3. country: Should be a string and one of the values in the countries array. You also pass a message as a second parameter to oneOf which will be the message that shows in case there's an error. It's also required.

  4. age: Should be a number and at least 18. If the age is less than 18, the message "You need to be older than 18 to register" will show. It's also required.

Running the output

Run the server again. Proceed to interact with the form. If you enter values that don't comply with the rules you set in the validation schema, an error will be displayed in red and you won't be able to submit the form until you fill the form correctly.

Custom Validation Rules

Although Yup has helpful validation rules that are readily available for developers, there is also flexibility for us to create custom validation rules. You can use the test function to add a custom rule.

In this section, you will learn how to add a rule to make sure that the name field has both first and last names.

Change the name property inside the validationSchema to the following:

const formik = useFormik({
    ...,
    validationSchema: Yup.object({
      name: Yup.string()
              .label('Full Name')
              .required()
              .test('is-full-name', 'Please enter both your first and last name', function (value) {
                const nameArr = value.split(" ");
                return nameArr.length >= 2;
              }),
      ...
    })
  })

The first parameter is the name of the custom rule. The second parameter is the message to show in case the field is invalid. The third parameter is the function that determines whether the field is valid or not. It should return a boolean value. If the value is true, then the field is valid. Otherwise, it's invalid.

Also, you validate the name field to contain both first and last names by splitting it on the space delimiter which will return an array. Proceed to check the array length, if it's at least 2, then the field is valid. Otherwise, it's invalid.

Run the server! If you enter one word in the Full Name field you'll see an error.

Run the server
Check if the validation rule you set is valid; inputting one word in the Full Name field should pop up an error. You need at least two words for the field to be valid.

Conclusion

Forms are an integral part of how users interact with our websites and web applications. Validating the data the user passes through the form is a critical aspect of our jobs as web developers. However, it doesn’t have to be a difficult process. In this article, you learned how to use Formik and Yup in React. You can use these two libraries to create forms, validate them, and handle their submission. Using these two libraries makes creating forms in React easier and less stressful.