React Hook Form Validation with Formik and Yup

A simple React Hook Form Validation with Formik and Yup

A Form is a very important component of any webpage. It allows developers to make the users directly input their data for various fields ranging from the login page to even a simple quiz test. It is just the replication of a simple form which you fill on paper into digital format with various fields, controls like radio buttons, checkboxes, and submit buttons. Thus, It is imperative to handle the form validation but it can be a little hassle, to solve this problem, it is where libraries like Formik or Yup come into the picture. 

Creating and handling the form and its validation through “React Way !!” could be frustrating as you have to create the process through which you have to manage the states and same time validates the data being inputted by the user. So lets us understand how Formik and Yup make this validation process easier.

What is Formik and Yup?

Formik

Formik is one of the most popular open-source libraries to handle form in React and React Native. There are other form management libraries too like Redux form or React Hook Form but what’s makes Formik stand out from other libraries is its simplicity !! As stated by its creator,

I (@jaredpalmer) wrote Formik while building a large internal administrative dashboard with @eonwhite. With around ~30 unique forms, it quickly became obvious that we could benefit by standardizing not just our input components but also the way in which data flowed through our forms

Advantages of Formik against other libraries

  • Declarative in nature, it reduces the developer’s effort through abstraction by itself handling the state, validation, and submissions.
  • Easy to understand and simple working, It does not use any fancy hook or complex management tool, if you understand React form, then you understand Formik, its just simple as that.
  • Scalable and adaptable, Formik does not any external state management libraries like Redux or MobX, it keeps everything local, thus reducing the latency unlike Redux Form, which calls Redux reducer at every keystroke.
  • Small size, It is very important to have small bundle size libraries because as your app component grows, it affects the app booting time, Formik minified gzipped is 12.7kb,

Yup

Even though Formik is fully capable to alone manage complex Form validations, handling synchronous and asynchronous level validation but it also supports schema-based form-validation through Yup. Yup as the documentation suggests is a JavaScript schema for validating and value parsing. It is inspired by Joi, but much simpler and client-side validation, as its primary use case.

Benefits of using Yup Schema

  • It is very simple to use, read and implement.
  • There are number of chain conditions and validation you can put to validate various fields like text, number or emails (i.e. number().required().positive()).
  • You can have the custom error messages according to the corresponding validation failure.
  • Yup supports data types such as string, boolean, object, date, and integer, all can be validated, making it simple for new developers to begin using it.

Also read, How to select only one element of a map() in React

Setting up the form and fields

Now without any delay, let us directly jump into setting up the form and install all necessary files and libraries, I will also add bootstrap for good UI,

npm i --save formik yup react-bootstrap bootstrap

and set up the basic form working with react hook, first, we will import the requisite components and create a function name, i.e. FormikYup(). We will also use the useState hook to hold data and submit function to check if the form is working perfectly fine or not,

import React,{useState} from "react";
import { Button, Form, Col } from "react-bootstrap";


export default function FormikYup() {

  const [name, setName] = useState("");
  const [profession, setProfession] = useState("");
  const [email, setEmail] = useState("");
  const [mobile, setMobile] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [address, setAddress] = useState("");
  const [terms, setTerms] = useState(false);
  
  const handleSubmit = (e) => {
    e.preventDefault();
    const values = {
      name:name,
      profession: profession,
      email:email,
      mobile:mobile,
      password:password,
      confirmPassword:confirmPassword,
      term:terms,
    }
    alert(JSON.stringify(values, null, 2));
  }

And, then we will add, all the form component,

<Form onSubmit={handleSubmit}>
  <Form.Row>
    <Form.Group as={Col} controlId="formGridName">
      <Form.Label>Full Name</Form.Label>
      <Form.Control
        type="text"
        placeholder="Enter Your Name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </Form.Group>
    <Form.Group as={Col} controlId="formGridProfession">
      <Form.Label>Profession</Form.Label>
      <Form.Control as="select" onChange={(e) => setProfession(e.target.value)}>
        <option value={null}>Choose...</option>
        <option value="Front End Developer">Front End Developer</option>
        <option value="Back End Developer">Back End Developer</option>
        <option value="Native App Developer">Native App Developer</option>
        <option value="Data Scientist">Data Scientist</option>
      </Form.Control>
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="formGridEmail">
      <Form.Label>Email</Form.Label>
      <Form.Control
        type="email"
        placeholder="Enter email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
    </Form.Group>

    <Form.Group as={Col} controlId="formGridPassword">
      <Form.Label>Mobile Number</Form.Label>
      <Form.Control
        type="number"
        placeholder="Enter your mobile number"
        value={mobile}
        onChange={(e) => setMobile(e.target.value)}
      />
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="formGridPassword1">
      <Form.Label>Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
    </Form.Group>

    <Form.Group as={Col} controlId="formGridPassword">
      <Form.Label>Confirm Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your confirm Password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
      />
    </Form.Group>
  </Form.Row>
  <Form.Group controlId="formGridAddress1">
    <Form.Label>Address</Form.Label>
    <Form.Control
      placeholder="1234 Main St"
      value={address}
      onChange={(e) => setAddress(e.target.value)}
    />
  </Form.Group>
  <Form.Group controlId="formBasicCheckbox">
    <Form.Check
      type="checkbox"
      label="I agree with all Terms and Conditions "
      checked={terms}
      onChange={(e) => setTerms(e.target.checked)}
    />
  </Form.Group>
  <Button variant="primary" type="submit">
    Sign Up
  </Button>
</Form>

And the form will look something like this, I have created various types of fields like email, phone number, and password so that you can have a good understanding of its working method.

formik

Also read, When Computers Create Code, Who Owns It Is a Question Worth Billions

Setting up useFormik

As you saw, we had to set up different hooks for each form’s fields but Formik provides a useFormik hook, that takes objects like (initialValues, validate, validationSchema and onSubmit), and has all the helper’s event handlers method like  (handleChangehandleBlur and handleSubmit) passed via props to help in managing your form. Note- handleChange and handleBlur work the same as in normal form, they use the name and id attribute to figure out which form’s filed to be updated.

It also works with the render approach method, so even if you are not using hooks, Formik will work smoothly without it too. In order to use useFormik, you need to import it, have initalValues according to your fields,

import React from "react";
import { Button, Form, Col } from "react-bootstrap";
import { useFormik } from "formik";

export default function FormikYup() {

  const formik = useFormik({
    initialValues: {
      name: '',
      profession : '',
      email: '',
      mobile: '',
      password: '',
      confirmPassword:'',
      address:'',
      terms:false
    },
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  })

And now we will change the name value with ‘formik.values.name’, inside onChange={formik.handleChange} and submit to ‘formik.handleSubmit’ in the body, will look something like this, remember to change the controlId if you are using React-bootstrap, to corresponding values,

  • Values – this is the object which holds the data of various input fields
  • handleChange – this function keeps in sync the changes with the formValues, and updates them accordingly.
  • handleSubmit – this function works as submit handler, and once the form is filled, you can do whatever according to your need.
<Form onSubmit={formik.handleSubmit}>
  <Form.Row>
    <Form.Group as={Col} controlId="name">
      <Form.Label>Full Name</Form.Label>
      <Form.Control
        type="text"
        placeholder="Enter Your Name"
        value={formik.values.name}
        onChange={formik.handleChange}
      />
    </Form.Group>
    <Form.Group as={Col} controlId="profession">
      <Form.Label>Profession</Form.Label>
      <Form.Control as="select" onChange={formik.handleChange}>
        <option value={null}>Choose...</option>
        <option value="Front End Developer">Front End Developer</option>
        <option value="Back End Developer">Back End Developer</option>
        <option value="Native App Developer">Native App Developer</option>
        <option value="Data Scientist">Data Scientist</option>
      </Form.Control>
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="email">
      <Form.Label>Email</Form.Label>
      <Form.Control
        type="email"
        placeholder="Enter email"
        value={formik.values.email}
        onChange={formik.handleChange}
      />
    </Form.Group>

    <Form.Group as={Col} controlId="mobile">
      <Form.Label>Mobile Number</Form.Label>
      <Form.Control
        type="number"
        placeholder="Enter your mobile number"
        value={formik.values.mobile}
        onChange={formik.handleChange}
      />
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="password">
      <Form.Label>Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your password"
        value={formik.values.password}
        onChange={formik.handleChange}
      />
    </Form.Group>

    <Form.Group as={Col} controlId="confirmPassword">
      <Form.Label>Confirm Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your confirm Password"
        value={formik.values.confirmPassword}
        onChange={formik.handleChange}
      />
    </Form.Group>
  </Form.Row>
  <Form.Group controlId="address">
    <Form.Label>Address</Form.Label>
    <Form.Control
      placeholder="1234 Main St"
      value={formik.values.address}
      onChange={formik.handleChange}
    />
  </Form.Group>
  <Form.Group controlId="terms">
    <Form.Check
      name="terms"
      type="checkbox"
      label="I agree with all Terms and Conditions "
      checked={formik.values.terms}
      onChange={formik.handleChange}
    />
  </Form.Group>
  <Button variant="primary" type="submit">
    Sign Up
  </Button>
</Form>

Formik Validation

Formik, provides validation and error objects, so if you don’t want to use any third-party library for validation then you can simply use these objects, One more important thing to note is, Formik also comes with components like <Form />, <Field />, and <ErrorMessage />, to have less verbose and reduce the boilerplate, they use react context to hook into the <Formik /> method. Lets us now add validation to our form and error messages accordingly.

  • touched – this object holds look over the various form fields which have been filled by the user or not.
  • validate – we pass the formValues object to this function, where you put validation of various fields, and populate the errors object accordingly.
  • errors – this object holds the error messages of various fields in value and message pair.
const validate = (values) => {
  let errors = {};
  const emailRegex = !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
  const passwordRegex = /(?=.*[0-9])/;
  const phoneRegex = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/;
  
	if (!values.name) {
	  errors.name = "Required";
	}
  
	if(!values.profession){
	  errors.profession = "Please Choose One";
	}
  
	if (!values.mobile) {
	  errors.mobile = 'Required';
	} else if (
	  phoneRegex.test(values.mobile)
	) {
	  errors.mobile = "Phone number is not valid";
	}
  
	if (!values.email) {
	  errors.email = 'Required';
	} else if (
	  emailRegex.test(values.email)
	) {
	  errors.email = 'Invalid email address';
	}
  
	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 = "Invalid password. Must contain one number.";
	}
  
	if (!values.confirmPassword) {
	  errors.confirmPassword = "Required";
	} else if(values.password !== values.confirmPassword){
	  errors.password = "Password does not match";
	  errors.confirmPassword = "Password does not match";
	}
  
	if (!values.address) {
	  errors.address = "Required";
	}
  
	if (!values.terms) {
	  errors.terms = "Required";
	}
  
	return errors;
  };
  
  export default function FormikYup() {
  
	const formik = useFormik({
	  initialValues: {
		name: '',
		profession : '',
		email: '',
		mobile: '',
		password: '',
		confirmPassword:'',
		address:'',
		terms:false
	  },
	  validate,
	  onSubmit: values => {
		alert(JSON.stringify(values, null, 2));
	  },
	})

As you see above, We are passing validate function which iterates through each value of our form, validates these values according to your need and returns the errors object which is a key pair, containing value and message. And to display these errors we will add a control feedback component available in bootstrap.

  • isInvalid – returns true, if there is any error and then you can display the error message in feedback control components.
<Form onSubmit={formik.handleSubmit}>
  <Form.Row>
    <Form.Group as={Col} controlId="name">
      <Form.Label>Full Name</Form.Label>
      <Form.Control
        type="text"
        placeholder="Enter Your Name"
        value={formik.values.name}
        onChange={formik.handleChange}
        isInvalid={formik.errors.name}
      />
      <Form.Control.Feedback type="invalid">
        {formik.errors.name}
      </Form.Control.Feedback>
    </Form.Group>
    <Form.Group as={Col} controlId="profession">
      <Form.Label>Profession</Form.Label>
      <Form.Control
        as="select"
        onChange={formik.handleChange}
        isInvalid={formik.errors.profession}
      >
        <option value={null}>Choose...</option>
        <option value="Front End Developer">Front End Developer</option>
        <option value="Back End Developer">Back End Developer</option>
        <option value="Native App Developer">Native App Developer</option>
        <option value="Data Scientist">Data Scientist</option>
      </Form.Control>
      <Form.Control.Feedback type="invalid">
        {formik.errors.profession}
      </Form.Control.Feedback>
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="email">
      <Form.Label>Email</Form.Label>
      <Form.Control
        type="email"
        placeholder="Enter email"
        value={formik.values.email}
        onChange={formik.handleChange}
        isInvalid={formik.errors.email}
      />
      <Form.Control.Feedback type="invalid">
        {formik.errors.email}
      </Form.Control.Feedback>
    </Form.Group>

    <Form.Group as={Col} controlId="mobile">
      <Form.Label>Mobile Number</Form.Label>
      <Form.Control
        type="number"
        placeholder="Enter your mobile number"
        value={formik.values.mobile}
        onChange={formik.handleChange}
        isInvalid={formik.errors.mobile}
      />
      <Form.Control.Feedback type="invalid">
        {formik.errors.mobile}
      </Form.Control.Feedback>
    </Form.Group>
  </Form.Row>
  <Form.Row>
    <Form.Group as={Col} controlId="password">
      <Form.Label>Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your password"
        value={formik.values.password}
        onChange={formik.handleChange}
        isInvalid={formik.errors.password}
      />
      <Form.Control.Feedback type="invalid">
        {formik.errors.password}
      </Form.Control.Feedback>
    </Form.Group>

    <Form.Group as={Col} controlId="confirmPassword">
      <Form.Label>Confirm Password</Form.Label>
      <Form.Control
        type="password"
        placeholder="Enter your confirm Password"
        value={formik.values.confirmPassword}
        onChange={formik.handleChange}
        isInvalid={formik.errors.confirmPassword}
      />
      <Form.Control.Feedback type="invalid">
        {formik.errors.confirmPassword}
      </Form.Control.Feedback>
    </Form.Group>
  </Form.Row>
  <Form.Group controlId="address">
    <Form.Label>Address</Form.Label>
    <Form.Control
      placeholder="1234 Main St....."
      value={formik.values.address}
      onChange={formik.handleChange}
      isInvalid={formik.errors.address}
    />
    <Form.Control.Feedback type="invalid">
      {formik.errors.address}
    </Form.Control.Feedback>
  </Form.Group>
  <Form.Group controlId="terms">
    <Form.Check
      name="terms"
      type="checkbox"
      label="I agree with all Terms and Conditions "
      checked={formik.values.terms}
      onChange={formik.handleChange}
      isInvalid={formik.errors.terms}
    />
    <Form.Control.Feedback type="invalid">
      {formik.errors.terms}
    </Form.Control.Feedback>
  </Form.Group>
  <Button variant="primary" type="submit">
    Sign Up
  </Button>
</Form>

And our form will look this,

formik validation

Also read, React Lifecycle Methods | Detail Explanation with Diagram

Yup Schema Validation

As stated, Formik gives you full freedom to choose any validation method or library to validate various form fields. But as you can see if we use Formik validation then we have to create conditions for each circumstance and field, which can be a huge task if you have a lot of multiple fields, so this where Yup comes to save us.

Instead of passing validation object, we pass Yup object schema validation. Formik has special option to pass Yup schema called validationSchema. It automatically try to matches values and try to convert into validation errors object. So to use Yup you have to import it, you can save the Yup in in different file or in same file,

import * as Yup from 'yup';

After importing it, you can write your validation schema,

const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/;


const RegistrationSchema = Yup.object().shape({
  name: Yup.string().min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),

  profession: Yup.string().required('Choose one'),

  email: Yup.string().email('Invalid email').required('Required'),

  mobile: Yup.string().matches(phoneRegExp, 'invalid number').required('Required'),

  password: Yup.string()
.required("Password is required")
    .min(6, "Password is too short - should be 6 chars minimum"),

  confirmPassword: Yup.string()
.oneOf([Yup.ref('password'), null], 'Passwords must match'),

  address:Yup.string().required('Required'),

  terms: Yup.boolean().oneOf([true])
})

And passing it in the formik object,

const formik = useFormik({
    initialValues:{
      name: '',
      profession : '',
      email: '',
      mobile: '',
      password: '',
      confirmPassword:'',
      address:'',
      terms:false
    },
    validationSchema: RegistrationSchema,
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  })

And here you can see, how Yup schema helps so much in reducing the validation boilerplate and makes your work easy. I am also attaching a live code demo on the code sandbox for your reference.

Also read, How Does React Js works | Detail Explanation on Virtual DOM

Final Words

In the end, for consumers or users, It doesn’t matter what type of validation you are using but It should make the developer’s job easy. And the error messages and requirements you need should be conveyed to the end-user properly.

I hope you like this article, and I was able to put all the details in simple and clear way. If you like this article then please share more to your friends and groups. I am trying to create a blog series for JavaScript and React, so please book mark this website to get all the awesome articles 😊.

Table of Contents