DEV Community

Cover image for Using Formik and Yup for Form validation
Taiwo Shobo
Taiwo Shobo

Posted on

Using Formik and Yup for Form validation

Form data validation can be a powerful tool for ensuring that your application's data meets the required criteria and is safe to use. However, it also comes with some pains, which are important to consider when implementing it. There are several issues to consider when creating a user experience for forms. These kinds of details distinguish between a bad experience and a good one. How, for instance, can the status of a form be changed? How about several validation methods? Setting up a form for submission can be verbose and be daunting sometimes.That's why I want to introduce Formik, small library that assists you with the three aspects that bother you the most:

  • Getting values in and out of form state is very pretty and straightforward
  • Handle form validation and error messages very well
  • Handling form submission within the local state

Formik allows us to handle changes and maintain state (values, errors, and if the form is being submitted). On each submission, it also calls the validation method for us.

In this article, we will briefly introduce Formik and Yup, what they are, and what you'd want to use them for. We will learn how they can build a signup form and show you how to use Formik and Yup for form validation.

About Formik and Yup

Formik is a versatile library, and it allows you to choose when and how much to utilize it. Additionally, how much of the Formik library's capabilities we use is within our control. Formik may be used with Yup and its unique components, HTML input fields, personal validation rules, or both. When using Formik, form validation becomes simple. Moreover, together with Yup, they abstract all of React's form processing challenges.

Yup, it is a validator for JavaScript object schema. We'll concentrate on how it makes it easier to develop custom validation rules so we don't have to, even if it has many valuable features. An example Yup object schema for a sign-up form is shown below.



import * as Yup from 'yup'

 const validationSchema = Yup.object().shape({
  email: Yup.string()
    .email('Invalid email address')
    .required('Email is required'),
  password: Yup.string()
    .required('Password is required')
    .min(8, 'Password must be at least 8 characters long')
    .matches(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
      'Password must contain at least one uppercase letter, one lowercase     letter, one digit, and one special character'
    ),
  confirmPassword: Yup.string()
    .oneOf([Yup.ref('password'), null], 'Passwords must match')
    .required('Confirm Password is required'),

})



Enter fullscreen mode Exit fullscreen mode

Prerequisite

  • A code editor (e.g. Visual Studio Code)
  • NodeJS (version 16 or higher)
  • Yarn (version 1.22.19)
  • Basic JavaScript and React

Getting started

You can install the dependencies/packages used in achieving this goal; these are the list of the dependencies.

  • Formik
  • Yup
  • React-toastify
  • TailwindCss

TailwindCss requires extra configuration. Check out the official documentation here

You can also check out the final code on GitHub.

yarn add formik yup react-toastify

There are three different approaches to work with formik

  • useFormik hook.
  • Formik with React context.
  • withFormik as Higher-order-component.

In this tutorial, we will consider the usage of the useFormik hook due to its conciseness in code structure and the ability to make code more readable and cleaner.

For the account signup application:

  • Create a simple form file for the signup application.
  • Include various HTML input elements for user input.

Creating a Signup React Application

Let's start by quickly scaffolding a React app using the create vite command.

yarn create vite

This prompts us to select the React development environment or the supporting frontend frameworks we want to use. Afterall, the yarn command is then used to install all of our dependent packages.

yarn

After installing the dependencies, we can now start our development environment.

yarn dev

We now have a working React demo app.

React demo app

Creating our Signup form application

First, creation of the custom validation function to validate and verify the input that has been passed on.



// src/validate.js

export const validate = (values) => {
  let errors = {}
  if (!values.email) {
    errors.email = 'This field is required'
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address'
  }
  if (!values.password) {
    errors.password = 'Please input your password'
  } else if (values.password.length < 8) {
    errors.password = 'The password must be greater than 8 characters'
  } else if (
    !/(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@#$%^&+=]).*$/.test(values.password)
  ) {
    errors.password =
      'Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character (@#$%^&+=)'
  }

  if (!values.confirmPassword) {
    errors.confirmPassword = 'Please confirm your password'
  } else if (values.confirmPassword !== values.password) {
    errors.confirmPassword = 'Passwords do not match'
  }

  return errors
}



Enter fullscreen mode Exit fullscreen mode


import { useFormik } from 'formik'
import { toast } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import { validate } from './validate'


import './App.css'


function App() {
  // Using custom validation

  const formik = useFormik({
    initialValues: {
      email: '',
      password: '',
      confirmPassword: '',
    },
    validate,
    onSubmit: (values, { resetForm }) => {
      try {
        console.log(JSON.stringify(values, null, 2))
        toast.success('Account created successfully', {
          position: toast.POSITION.TOP_RIGHT,
        })
        resetForm()
      } catch (error) {
        console.log('Error occured', error.message)
      }
    },
  })
  return (
    <>
      <section className='bg-gray-50 dark:bg-gray-900'>
        <div className='flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0'>
          <div className='w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700'>
            <div className='p-6 space-y-4 md:space-y-6 sm:p-8'>
              <h1 className='text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white'>
                Create an account
              </h1>
              <form
                onSubmit={formik.handleSubmit}
                className='space-y-4 md:space-y-6'
              >
                {/*
                 *****
                 */}
                <button
                  type='submit'
                  className='w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800'
                >
                  Create an account
                </button>
              </form>
            </div>
          </div>
        </div>
      </section>
    </>
  )
}

export default App



Enter fullscreen mode Exit fullscreen mode

In the code above, here is a thorough explanation of it.

  • The initialValues property of the useFormik hook is an object that defines the initial values for the form fields. Here, a set of empty strings are being passed to the field names "email, password, confirmPassword" to indicate that inputs are expected in the forms.

  • The validate function is exported from a separate file (validate.js), and it is used to validate form input values. It checks whether the email, password, and confirmPassword fields meet specific criteria and returns an object with error messages if validation fails.

  • Below each input element, there are conditional error messages displayed if the field has been touched (formik.touched), and validation failed (formik.errors).

  • The code utilizes the toast library to display a toast message when the form is successfully submitted.

  • resetForm is called within the onSubmit handler function, allowing you to reset the form's fields to their initial values once the form has been processed.

Writing custom form validation can be complex and time-consuming, especially for forms with multiple fields and intricate validation rules. It involves writing and maintaining custom JavaScript code for each validation scenario.

On the other hand, Yup offers a declarative way to define validation schemas using a simple and expressive syntax. This makes it easier to specify validation rules for each form field.

Validation with Yup

Yup offers a simple but effective method for validating an object schema for your form controls.

Using Formik, form validation is simple. Together with Yup, they abstract all React's form processing challenges. With that, we can use Yup to generate the schema for the sign-in form.

Yup take care of the custom validations instead of doing it ourselves, which might be time consuming depending on how many fields there are.

Take a look at this code snippet for our signup



src/schema.js

import * as Yup from 'yup'

export const validationSchema = Yup.object().shape({
  email: Yup.string()
    .email('Invalid email address')
    .required('Email is required'),
  password: Yup.string()
    .required('Password is required')
    .min(8, 'Password must be at least 8 characters long')
    .matches(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
      'Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character'
    ),
  confirmPassword: Yup.string()
    .oneOf([Yup.ref('password'), null], 'Passwords must match')
    .required('Confirm Password is required'),
})



Enter fullscreen mode Exit fullscreen mode

Formik has a unique configuration prop for Yup called validationSchema that will automatically transform Yup's validation error messages into a pretty object whose keys match values/initial values/touched (just like any custom validation function would have to do). This is because Formik authors and users adore Yup so much.

Let's replace our custom validation function with validationSchema to demonstrate how Yup works.

Exporting the Yup schema into our form, replacing the custom validation function



import { useFormik } from 'formik'
import { toast } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import { validationSchema } from './schema'
import './App.css'


function App() {
  const formik = useFormik({
    initialValues: {
      email: '',
      password: '',
      confirmPassword: '',
    },
    validationSchema: validationSchema,
    onSubmit: (values, { resetForm }) => {
      try {
        console.log(JSON.stringify(values, null, 2))
        toast.success('Account created successfully', {
          position: toast.POSITION.TOP_RIGHT,
        })
        resetForm()
      } catch (error) {
        console.log('Error occured', error.message)
      }
    },
  })
  return (
    <>
      <section className='bg-gray-50 dark:bg-gray-900'>
        <div className='flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0'>
          <div className='w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700'>
            <div className='p-6 space-y-4 md:space-y-6 sm:p-8'>
              <h1 className='text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white'>
                Create an account
              </h1>
              <form
                onSubmit={formik.handleSubmit}
                className='space-y-4 md:space-y-6'
              >
                {/*
                 *****
                 */}
                <button
                  type='submit'
                  className='w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800'
                >
                  Create an account
                </button>
              </form>
            </div>
          </div>
        </div>
      </section>
    </>
  )
}

export default App



Enter fullscreen mode Exit fullscreen mode

Congratulations! You and Formik have produced a signup form that has intricate validation logic, and many error messages send error notifications to users in the proper context and at the appropriate moment

Conclusion

We've successfully examined the useFormik hook for form validation. We have shown how Formik may be used gradually and how Yup and Formik work together to handle form validation.

Top comments (0)