Formik with React

Got Formik? Yup!

Chris Berry Development Technologies, JavaScript, React, Tutorial 3 Comments

Attention: The following article was published over 4 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

Let us present a scenario. You’re building a React application and you’ve been tasked to build a registration form.

It seems simple enough, you think at first, but then you start hearing the requirements. The application needs to have validation on each field, and each field needs to have certain requirements, such as being required, being a certain type of field, having a certain length, etc. Your form must also be easily created and have an easily-adjusted state during its usage.

At this point, something you originally thought would be quick and easy is seeming more and more time consuming and complicated! Well, worry no more and look no further; the dev community has come to your rescue.

In this post, we’re going to talk about using the form library called Formik and the validation library Yup to build out a simple validation form inside of a ReactJS application. You’ll see a full Formik implementation and then we’ll break it down piece by piece.

Formik For Your Needs

Formik describes itself as a way to take “care of the repetitive and annoying stuff – keeping track of values/errors/visited fields, orchestrating validation, and handling submission – so you don’t have to. This means you spend less time wiring up state and change handlers and more time focusing on your business logic.”

Let us take a look at a full Formik implementation and break down what you will need when using this library.

import React, {useState} from "react";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import toastr from 'toastr'
import 'toastr/build/toastr.min.css'

export default function Registration() {
  const [isSubmitting, setSubmitting] = useState(false);
  return (
    <>
      <Formik
       initialValues={{
          firstName: "",
          lastName: "",
          email: "",
          password: "",
          confirmPassword: ""
        }}

        validationSchema={Yup.object().shape({
          firstName: Yup.string().required("First Name is required"),
          lastName: Yup.string().required("Last Name is required"),
          email: Yup.string().email("Email is invalid").required("Email is required"),
          password: Yup.string().min(6, "Password must be at least 6 characters").required("Password is required"),
          confirmPassword: Yup.string()
            .oneOf([Yup.ref("password"), null], "Passwords must match")
            .required("Confirm Password is required")
        })}

          onSubmit={(values, {setSubmitting, resetForm}) => {
            toastr.options = {
              positionClass : 'toast-bottom-left',
              hideDuration: 300,
              timeOut: 60000
            }
             toastr.clear()
            // When button submits form and form is in the process of submitting, submit button is disabled
            setSubmitting(true);
            setTimeout(function(){ 
              console.log("SUCCESS: ", JSON.stringify(values, null, 4));
              toastr.success("Success! Registration has occurred.");
             }, 3000);
                       
            // Resets form after submission is complete
            resetForm();
            // Sets setSubmitting to false after form is reset
            setSubmitting(false);
        }}
      >
      {({ errors, status, touched, isSubmitting})=> 
       <Form>
            <div className="form-group">
              <label htmlFor="firstName">First Name</label>
              <Field
                name="firstName"
                type="text"
                className={
                  "form-control" +
                  (errors.firstName && touched.firstName ? " is-invalid" : "")
                }
              />
              <ErrorMessage
                name="firstName"
                component="div"
                className="invalid-feedback"
              />
            </div>
            <div className="form-group">
              <label htmlFor="lastName">Last Name</label>
              <Field
                name="lastName"
                type="text"
                className={
                  "form-control" +
                  (errors.lastName && touched.lastName ? " is-invalid" : "")
                }
              />
              <ErrorMessage
                name="lastName"
                component="div"
                className="invalid-feedback"
              />
            </div>
            <div className="form-group">
              <label htmlFor="email">Email</label>
              <Field
                name="email"
                type="text"
                className={
                  "form-control" +
                  (errors.email && touched.email ? " is-invalid" : "")
                }
              />
              <ErrorMessage
                name="email"
                component="div"
                className="invalid-feedback"
              />
            </div>
            <div className="form-group">
              <label htmlFor="password">Password</label>
              <Field
                name="password"
                type="password"
                className={
                  "form-control" +
                  (errors.password && touched.password ? " is-invalid" : "")
                }
              />
              <ErrorMessage
                name="password"
                component="div"
                className="invalid-feedback"
              />
            </div>
            <div className="form-group">
              <label htmlFor="confirmPassword">Confirm Password</label>
              <Field
                name="confirmPassword"
                type="password"
                className={
                  "form-control" +
                  (errors.confirmPassword && touched.confirmPassword
                    ? " is-invalid"
                    : "")
                }
              />
              <ErrorMessage
                name="confirmPassword"
                component="div"
                className="invalid-feedback"
              />
            </div>
            <div className="form-group">
              <button type="submit" className="btn btn-primary mr-2" disabled={isSubmitting}>
                Register
              </button>
              <button type="reset" className="btn btn-secondary">
                Reset
              </button>
            </div>
          </Form>
      }
      </ Formik>
    </>
  );
}

Breaking It Down

I will explain the code from top to bottom:

First, we need to import the necessary dependencies for the registration form. We’re going to have two specific ones for this form which are:

import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";

From Formik, we have imported Field, Form, ErrorMessages, and Formik itself, all of which are used to handle different parts of our form. Next, we have imported Yup, which is a schema-validation library. You use it to define a JavaScript schema and then validate an arbitrary object against that schema. Yup provides you with a full set of standard schema validations like required, min length, max length, email, etc.

The file is a normal React functional component, where we have created a Formik container. This will define our initial field values or validation and how the form will handle submissions. Inside the opening Formik element, we start with the initial values:

 initialValues={{
          firstName: "",
          lastName: "",
          email: "",
          password: "",
          confirmPassword: ""
        }}

This object is representing how the fields will be grouped together and used when a submission occurs.

Now we move down to where the validation scheme is declared.

 validationSchema={Yup.object().shape({
     firstName: Yup.string().required("First Name is required"),
     lastName: Yup.string().required("Last Name is required"),
     email: Yup.string().email("Email is invalid").required("Email is required"),
     password: Yup.string().min(6, "Password must be at least 6
 	characters").required("Password is required"),
       confirmPassword: Yup.string()
       .oneOf([Yup.ref("password"), null], "Passwords must match")
       .required("Confirm Password is required")
  })}

Here, we’re using the power of Yup to greatly simplify the requirements of custom validation. The pattern is pretty straightforward.

First, we declare the field name. We tell Yup what kind of field it will be, and then, we can use chained methods to add additional validations. The password field is a good example of this chaining of validations. You can see that this field has a minimum length requirement, is required, and must have a matching confirmed password. Strings declared are used for the custom messages which are displayed on the field when errors occur.

Our last bit of code within the opening element is for setting up the submission.

 onSubmit={(values, {setSubmitting, resetForm}) => {
       toastr.options = {
          positionClass : 'toast-bottom-left',
          hideDuration: 300,
          timeOut: 60000
        }
       toastr.clear()
 // When button submits form and form is in the process of submitting, submit button is disabled
      setSubmitting(true);
      setTimeout(function(){ 
       console.log("SUCCESS: ", JSON.stringify(values, null, 4));
          toastr.success("Success! Registration has occured.");
       }, 3000);
                       
       // Resets form after submission is complete
       resetForm();
       // Sets setSubmitting to false after form is reset
           setSubmitting(false);
  }}

The code here is pretty straightforward. A toastr element has been declared here for the user to get feedback during the submission. A JavaScript timeout is wrapping a console log to simulate a call to a server. However, notice that we are using a variable called values. This variable holds the actual values from our form. It’s passed in at the beginning of the onSubmit function and can be used throughout the function as needed.

So far, we have completed setting up our initial values, our validations, and what happens upon submission. Now, we’re ready to create our form.

Using Formik in the Field

To use Formik in the actual form creation, we must create a < Form > wrapper, which was brought in from Formik. Each form field looks something similar to this:

<div className="form-group">
              <label htmlFor="firstName">First Name</label>
              <Field
                name="firstName"
                type="text"
                className={
                  "form-control" +
                  (errors.firstName && touched.firstName ? " is-invalid" : "")
                }
              />
              <ErrorMessage
                name="firstName"
                component="div"
                className="invalid-feedback"
              />
            </div>

We use Formik’s Field component to define what type of field this is, the name of field, and the CSS classes that are going to be used on the field. In this case, we have a ternary statement to handle showing an error class or an empty class based upon Formik’s errors and touched values being passed in during runtime. The last part of a field is to handle showing an error. Formik provides an ErrorMessage component. We have added it here again with the name, the class names, and the type of component used for the display. Each field used on the form follows the same pattern.

At the bottom of the form, we have a simple submit Button element declared, and it will fire off the default implementation of the form submit. This submission will fall back onto the onSubmit function we declared earlier and submit the form as coded.

To use this form within a React application, you need to only import the form:

import Registration from "./Registration";

Then, utilize the form component within the JSX markup of the application:

<Registration />

Wrapping It Up

We now have a fully functional web form which is powered by Formik and validated by Yup. While this was a simple example, it will give you a glimpse at the power of what Formik combined with Yup can do for you as your application grows.

You also might check out this related post from Keyhole’s Mat Warger – The Joy of Forms with React and Formik.

5 1 vote
Article Rating
Subscribe
Notify of
guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments