While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

Introduction

In order to ensure that the form element of your web application is returning valid data, it is helpful to build automated validation into your code. This is true in React as well; creating form validation early on can often save you from encountering errors down the road.

In React, working with and validating forms can be a bit verbose. To make your code more manageable, you could use a package like Formik to build your forms.

In this tutorial, you will create a React project, add the Formik package, customize the Formik component with an onSubmit callback and a validate function for error messages, then display those error messages to the user.

By the end of this tutorial, you will have a project like this live example on CodeSandbox.

Step 1 — Creating the React Project

For this demo, we’ll be using CodeSandbox. You can use CodeSandbox as well or use your local environment.

Regardless of what you use for this demo, start with a new React app using Create React App:

Choosing to create a React app in CodeSandbox

Step 2 — Installing Dependencies

Now that we have our initial project created, we will install three packages:

  • Formik - makes handling validation, error messages, and form submission more manageable.
  • Email-validator - tiny package to validate emails.
  • Yup - schema validator that is commonly used in conjunction with Formik.

Formik

npm landing page for the Formik package

In your terminal, install Formik:

  • npm install Formik

In CodeSandbox, add this to the dependency GUI:

Adding Formik to the CodeSandbox Dependency GUI

email-validator

npm landing page for the email-validator package

Now, install email-validator:

  • npm install email-validator

Again, install this from the CodeSandbox GUI if you are using CodeSandbox:

Adding email-validator to the CodeSandbox Dependency GUI

Yup

npm landing page for the Yup package

Install Yup with this command:

  • npm install Yup

Add it to your CodeSandbox Dependency GUI if you are using CodeSandbox:

Adding Yum to the CodeSandbox Dependency GUI

Step 3 — Creating the Validated Form Component

Now, we can start to write our ValidatedFormComponent. For now, we will create the basics and import it into the root file in the app to display it.

To do this, we will:

  • Create a new functional component
  • Add dummy display content
  • Import it into index.js

Create a new file in your src directory called ValidatedLoginForm. js. Inside of that file, add the basic code for a functional component:

import React from "react";
const ValidatedLoginForm = () => (
  <div>
    <h1>Validated Form Component</h1>
  </div>
);

export default ValidatedLoginForm;

Then, include it in your index.js file.

function App() {
  return (
    <div className="App">
      <ValidatedLoginForm />
    </div>
  );
}

You will see it displayed:

Validated Form Component in CodeSandbox

Now, let’s start with Formik. First, import Formik, Email-Valiator, and Yup in your new component:

import { Formik } from "formik";
import _ as EmailValidator from "email-validator";
import _ as Yup from "yup";

Now, let’s write the Formik tag with initial values. Think of initial values as setting your state initially.

You’ll also need an onSubmit callback. This callback will take two parameters, values and an object that we can destructure. The values represent the input values from your form. We will add some dummy code here to simulate an async login call, then log out what the values are.

In the callback, call the setSubmitting function that was destructured from the second parameters. This will allow us to enable/disable the submit button while the asynchronous login call is happening:

<Formik
  initialValues={{ email: "", password: "" }}
  onSubmit={(values, { setSubmitting }) => {
    setTimeout(() => {
      console.log("Logging in", values);
      setSubmitting(false);
    }, 500);
  }}
>
  <h1>Validated Login Form</h1>
</Formik>

Render Props

The Formik component uses render props to supply certain variables and functions to the form that we create.

In short, render props are used to pass properties to children elements of a component. In this case, Formik will pass properties to our form code, which is the child. Notice that we’re using destructuring to get a reference to several specific variables and functions.

    { props => {
      const {
        values,
        touched,
        errors,
        isSubmitting,
        handleChange,
        handleBlur,
        handleSubmit
      } = props;
      return (
        <div>
          <h1>Validated Login Form</h1>
        </div>
      );
    }}

Step 4 — Displaying the Form

Now, we can start to write the code to display the form. The form will have two inputs (email and password), labels for each, and a submit button.

{ props => {
      const {
        values,
        touched,
        errors,
        isSubmitting,
        handleChange,
        handleBlur,
        handleSubmit
      } = props;
      return (
        <form onSubmit={handleSubmit}>
          <label htmlFor="email">Email</label>
          <input name="email" type="text" placeholder="Enter your email" />

          <label htmlFor="email">Password</label>
          <input
            name="password"
            type="password"
            placeholder="Enter your password"
          />
          <button type="submit" >
            Login
          </button>
        </form>
      );
    }}

Notice that the onSubmit is calling the handleSubmit from the props.

Earlier, we mentioned that we could disable our submit button while the user is already attempting to log in. We can add that change now by using the isSubmitting property that we destructured from the previous props:

  <button type="submit" disabled={isSubmitting}>
      Login
  </button>

Next, add the CSS from the finished CodeSandbox. You can use the following CSS for your styles.css file:

.App {
  font-family: sans-serif;
}

h1 {
  text-align: center;
}

form {
  max-width: 500px;
  width: 100%;
  margin: 0 auto;
}

label,
input {
  display: block;
  width: 100%;
}

label {
  margin-bottom: 5px;
  height: 22px;
}

input {
  margin-bottom: 20px;
  padding: 10px;
  border-radius: 3px;
  border: 1px solid #777;
}

input.error {
  border-color: red;
}

.input-feedback {
  color: rgb(235, 54, 54);
  margin-top: -15px;
  font-size: 14px;
  margin-bottom: 20px;
}

button {
  padding: 10px 15px;
  background-color: rgb(70, 153, 179);
  color: white;
  border: 1px solid rgb(70, 153, 179);
  background-color: 250ms;
}

button:hover {
  cursor: pointer;
  background-color: white;
  color: rgb(70, 153, 179);
}

Step 5 — Adding Validation Messages Logic

Now let’s validate our inputs. The first step is to determine what constraints we want to have on our input. Let’s start with email. Email input should:

  • Be required
  • Look like a real email

Password input should:

  • Be required
  • Be at least eight characters long
  • contain at least one number

We’ll cover two ways to create these messages, one doing it manually and one using Yup.

Manual Method

The first option is to create our validate function ourselves. The purpose of the function is to iterate through the values of our form, validate these values in whatever way we see fit, and return an errors object that has key value pairs of value and message.

Inside of the Formik tag, add the following code. This will always add an “Invalid email” error for email.

    validate={values => {
      let errors = {};
      errors.email = "Invalid email";
      return errors;
    }}

Now, we can ensure that the user has input something for the email:

validate={values => {
      let errors = {};
      if (!values.email) {
        errors.email = "Required";
      } 
 return errors;
}}

Then, we can check that the email is a valid-looking email by using the email-validator package. This will look almost the same as the equivalent check for email.

  validate={values => {
      let errors = {};
      if (!values.email) {
        errors.email = "Required";
      } else if (!EmailValidator.validate(values.email)) {
        errors.email = "Invalid email address";
      }
      return errors;
    }}

That takes care of email, so now for password. We can first check that the user input something.

validate={values => {
      let errors = {};
      if (!values.password) {
        errors.password = "Required";
      } 
 return errors;
}}

Now we need to check the length to be at least eight characters.

validate={values => {
      const passwordRegex = /(?=.*[0-9])/;
      if (!values.password) {
        errors.password = "Required";
      } else if (values.password.length < 8) {
        errors.password = "Password must be 8 characters long.";
      } 

      return errors;
    }}

Lastly, check that the password contains at least one number. For this, we can use regex.

 validate={values => {
      let errors = {};

      const passwordRegex = /(?=.*[0-9])/;
      if (!values.password) {
        errors.password = "Required";
      } else if (values.password.length < 8) {
        errors.password = "Password must be 8 characters long.";
      } else if (!passwordRegex.test(values.password)) {
        errors.password = "Invalida password. Must contain one number";
      }

      return errors;
    }}

Here’s the whole thing:

  validate={values => {
      let errors = {};
      if (!values.email) {
        errors.email = "Required";
      } else if (!EmailValidator.validate(values.email)) {
        errors.email = "Invalid email address";
      }

      const passwordRegex = /(?=.*[0-9])/;
      if (!values.password) {
        errors.password = "Required";
      } else if (values.password.length < 8) {
        errors.password = "Password must be 8 characters long.";
      } else if (!passwordRegex.test(values.password)) {
        errors.password = "Invalida password. Must contain one number";
      }

      return errors;
    }}

Using Yup

You might have noticed that handling the validate logic on our own gets a bit verbose. We have to manually do all of the checks ourselves. Yup can save us some of this work. When using Yup, we no longer will see the Validate property, but instead use validationSchema. Let’s start with email. Here is the equivalent validation using Yup:

validationSchema={Yup.object().shape({
      email: Yup.string()
        .email()
        .required("Required")
    })}

Now, for password:

validationSchema={Yup.object().shape({
  email: Yup.string()
    .email()
    .required("Required"),
  password: Yup.string()
    .required("No password provided.")
    .min(8, "Password is too short - should be 8 chars minimum.")
    .matches(/(?=.*[0-9])/, "Password must contain a number.")
})}

Step 6 — Displaying Validation/Error Messages

Now that we have the logic for creating error messages, we need to display them. We will need to update the inputs in our form a bit.

We need to update several properties for both email and password inputs:

  • value
  • onChange
  • onBlur
  • className

Email

Let’s start by updating value, onChange, and onBlur. Each of these will use properties from the render props:

<input
  name="email"
  type="text"
  placeholder="Enter your email"
  value={values.email}
  onChange={handleChange}
  onBlur={handleBlur}
/>

Then we can add a conditional “error” class if there are any errors. We can check for errors by looking at the errors object.

We can also check the touched property, to see whether or not the user has interacted with the email input before showing an error message.

<input
  name="email"
  type="text"
  placeholder="Enter your email"
  value={values.email}
  onChange={handleChange}
  onBlur={handleBlur}
  className={errors.email && touched.email && "error"}
/>

Lastly, if there are errors, we will display them to the user. All in all, email will look like this:

<label htmlFor="email">Email</label>
<input
  name="email"
  type="text"
  placeholder="Enter your email"
  value={values.email}
  onChange={handleChange}
  onBlur={handleBlur}
  className={errors.email && touched.email && "error"}
/>
{errors.email && touched.email && (
  <div className="input-feedback">{errors.email}</div>
)}

Password

Now we need to do the same with password. These steps are similar to email; here’s the final code:

<label htmlFor="email">Password</label>
<input
  name="password"
  type="password"
  placeholder="Enter your password"
  value={values.password}
  onChange={handleChange}
  onBlur={handleBlur}
  className={errors.password && touched.password && "error"}
/>
{errors.password && touched.password && (
  <div className="input-feedback">{errors.password}</div>
)}

Step 7 — Testing It Out

Now let’s try it out. You can start by clicking the button without entering anything. You will see validation messages:

Validation messages in the login form

Now, we can get more specific for testing messages. Refresh your page to do this. Click inside of the email input, but don’t type anything:

Focusing the email field in the login form

Then, click away from the input. You should see the Required message pop up. Notice that this message doesn’t pop up automatically when the page loads. We only want to display error messages after the user has interacted with the input.

Email validation error in the login form

Now, start to type. You should get a message about not being a valid email.

Invalid email message in the login form

Lastly, type in a valid email, and watch your error message go away.

Valid email in the login form

Now, do the same for password. Click on the input, then away, and you’ll get the required message.

The required password message in the login form

Then, start typing and you’ll see the length validation.

Length validation message in the login form

Then, type eight or more characters that do not include a number, and you’ll see the must contain a number message.

"must contain a number" message in the login form

Finally, add a number, and the error messages go away.

Valid password in the login form

Conclusion

You’ve now created a form with automatic validation in React, using Formik and Yum. For more tutorials on React, check out our React Topic page.

0 Comments

Creative Commons License