February 10, 2023

React Form + Formik + Yup Validation Example

In the post React Form Using Formik's useFormik() Hook we have seen how to create a form in React using Formik library and how to do validation. In that post we used useFormik() hook and did the validation ourselves. Formik provides integration with Yup for object schema validation.

Formik has a special configuration prop for Yup called validationSchema which will automatically transform Yup's validation errors messages into an object whose keys match values/initialValues/touched so that it is easy to map error message with the corresponding form field.

Installing Yup

you can install Yup from NPM or yarn

With npm

npm install yup -save

With yarn

yarn add yup

Using Formik component

To further reduce code and make it more readable Formik comes with components like <Formik />, <Form />, <Field />, and <ErrorMessage />. These formik components use React Context implicitly.

React form example with Formik and Yup

In this example we'll create a form with 5 fields- First Name, Last Name, Join Date, Email and Employment Type.

You can swap useFormik() hook with Formik component, with that you can pass initial values for the formFields, validation schema and a submission function.

<Formik 
  initialValues= {{
      firstName: '',
      lastName: '',
      joinDate: '',
      email: '',
      employmentType: 'permanent'
  }}
  validationSchema={Yup.object({
      firstName: Yup.string()
      .max(10, 'Must be 10 characters or less')
      .required('Required'),
      lastName: Yup.string()
      .max(15, 'Must be 15 characters or less')
      .required('Required'),
      joinDate:  Yup.date().max(new Date(), "Join date can't be greater than current date").required('Required'),
      email: Yup.string().email('Invalid email address').required('Required'),
  })}
  onSubmit={(values, { setSubmitting }) => {
      setTimeout(() => {
      console.log(JSON.stringify(values, null, 2));
      setSubmitting(false);
      }, 400);
  }}
>

By using Field component you don't need to pass event handlers- onChange, onBlur, value explicitly.

<Field type="text" name="firstName" />

Here is the full React form example using Formik, Field, Form, ErrorMessage components and Yup for validation.

import { ErrorMessage, Field, Form, Formik } from "formik";
import * as Yup from 'yup';
const EmployeeInfoForm = () => {
  return(
    <Formik 
      initialValues= {{
          firstName: '',
          lastName: '',
          joinDate: '',
          email: '',
          employmentType: 'permanent'
      }}
      validationSchema={Yup.object({
          firstName: Yup.string()
          .max(10, 'Must be 10 characters or less')
          .required('Required'),
          lastName: Yup.string()
          .max(15, 'Must be 15 characters or less')
          .required('Required'),
          joinDate:  Yup.date().max(new Date(), "Join date can't be greater than current date").required('Required'),
          email: Yup.string().email('Invalid email address').required('Required'),
      })}
      onSubmit={(values, { setSubmitting }) => {
          setTimeout(() => {
          console.log(JSON.stringify(values, null, 2));
          setSubmitting(false);
          }, 400);
      }}
    >
    {({ errors, touched }) => (
      <Form>

        <div className="form-group col-sm-4 mb-2" >
            <label htmlFor="firstName" className="form-label">First Name</label>
            <Field type="text" name="firstName" placeholder="Enter firstname" 
            className={'form-control' + (errors.firstName && touched.firstName ? ' is-invalid' : '')} />
            <ErrorMessage name="firstName" component="div" className="invalid-feedback" />       
        </div>
        <div className="form-group col-sm-4 mb-2" >
            <label htmlFor="lastName" className="form-label">Last Name</label>
            <Field type="text" name="lastName" className={'form-control'+ (errors.lastName && touched.lastName ? ' is-invalid' : '')}/>
            <ErrorMessage name="lastName" component="div" className="invalid-feedback"/>  
        </div>
        <div className="form-group col-sm-4 mb-2" >
            <label htmlFor="joinDate" className="form-label">Join Date</label>
            <Field type="date" name="joinDate" className={'form-control'+ (errors.joinDate && touched.joinDate ? ' is-invalid' : '')}/>
            <ErrorMessage name="joinDate" component="div" className="invalid-feedback"/>  
        </div>
        <div className="form-group col-sm-4 mb-2" >
            <label htmlFor="email" className="form-label">Email</label>
            <Field type="email" name="email" className={'form-control'+ (errors.email && touched.email ? ' is-invalid' : '')}/>
            <ErrorMessage name="email" component="div" className="invalid-feedback"/>  
        </div>
        <div className="form-group col-sm-4 mb-2" >
            <label htmlFor="employmentType" className="form-label">Employment Type:</label>
            <Field as="select" name="employmentType" className="form-select " >              
                <option value="permanent">Permanent</option>
                <option value="contract">Contract</option>
            </Field>
        </div>
          <div className="form-group col-sm-4 mb-2" >
              <button type="submit" className="btn btn-primary">Submit</button>
          </div>          
      </Form>
     )}
    </Formik>
  );
}

export default EmployeeInfoForm;

Some important points to note here-

  1. In the example Bootstrap 5 is used for styling. You can import Bootstrap by adding following to the <head> section of the index.html file.
    <link rel="stylesheet" href=https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous" />
    
  2. Initial values for the form fields are as empty fields except for the employmentType which is a drop-down list where pre-selected option is given.
  3. Validation is done using Yup which is quite self-explanatory. For join date it should not go beyond the current date.
  4. The submission function (onSubmit) logs the form values.
  5. In the <Form> section some of the Bootstrap classes like form-group, form-label, form-control, is-invalid, invalid-feedback are used.
  6. If there is an error then apart from form-control, is-invalid class is also added.
  7. The <Field> component by default will render an <input> component. <Field> also accepts a few other props to let you render other elements like textarea, select.
    <Field name="message" as="textarea" />
    
    <Field name="employmentType" as="select"  />  
    

With Error Messages

React form with Formik

With All Form Values

React form Formik and Yup

That's all for the topic React Form + Formik + Yup Validation Example. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment