File Validation in React with Formik and Yup
Tags: react, tutorial, frontend, javascript
Published: 2023-08-06 15:54:24

Dealing with forms in React applications used to be tedious, but with libraries like [Formik](https://formik.org/docs/overview) and [Yup](https://www.npmjs.com/package/yup), handling form state and validating each field is much easier and efficient. Formik does the overall form management and Yup helps to define the structure and rules to be applied to the form.

When working with file inputs, schema building and validation is not as straightforward as validating a text field, for example. This guide will show how to implement that in a React application.

<p>&nbsp;</p>

## Step 1: Installing Dependencies
To install Formik and Yup, run this on your command line:

```
npm install formik yup
```

If you're using yarn, do this instead:

```
yarn add formik yup
```
<p>&nbsp;</p>

## Step 2: Handling Form State
The form should consist of a field with an input of type 'file' and a submit button. At this point, the form looks like this:

```
const MyForm = () => {
  return (
    <form>
      <input type='file' name='myFile' accept='.pdf'/>
      <button type='submit'>Submit Form</button>
    </form>
  )
}

export default MyForm;
```

There are different ways to handle form state with Formik, but in this guide we'll be using the `useFormik` hook to directly provide all the Formik helpers needed. Here we can define the initial values of the input field, what happens when the form is submitted, and a validation schema.

```
import { useFormik } from 'formik';

const MyForm = () => {
  const formik = useFormik({
    initialValues: {
      myFile: ''
    },
    onSubmit: () => {
      console.log('Form submitted!')
    }
  })
  return (
    <form>
      <input type='file' name='myFile' accept='.pdf'/>
      <button type='submit'>Submit Form</button>
    </form>
  )
}

export default MyForm;
```

<p>&nbsp;</p>

## Step 3: Building the Validation Schema
Next, import Yup into the file to define the set of rules you want the form field to obey. In this guide, we'll be validating the file input in two aspects:

* The field must be required
* The file must only accept PDF files
* The file size must not be more than 3 megabytes

Here's what the validation schema looks like:

```
import * as Yup from 'yup';

const validationRules = Yup.object().shape({
    myFile: Yup.mixed().required('required')
      .test('fileFormat', 'Only PDF files are allowed', value => {
        if (value) {
          const supportedFormats = ['pdf'];
          return supportedFormats.includes(value.name.split('.').pop());
        }
        return true;
      })
      .test('fileSize', 'File size must be less than 3MB', value => {
        if (value) {
          return value.size <= 3145728;
        }
        return true;
      }),
  })
```

When validating the file type, we use the `test` function to determine whether the file extension is `.pdf`. We also have to test if the file size is not more than 3,145,728 bytes (3 megabytes).

<p>&nbsp;</p>

## Step 4: Displaying Errors in Field
We intend to display the validation errors below the field, so we render a paragraph just below the file input:

```
<form onSubmit={formik.handleSubmit}>
      <input type='file' name='myFile' accept='.pdf' onChange={handleChange}/>

      <div>{(formik.errors.myFile) ? <p style={{color: 'red'}}>{formik.errors.myFile}</p> : null}</div>
      <br/>
      <button type='submit'>Submit Form</button>
    </form>
```


To make sure that Formik is actually in charge of the field's state, a function is passed to the input's `onChange` property. The function sets the value of the field to Formik's state by using the helper function `setFieldValue`, a function Formik provides to set a field's value to something. It takes two arguments: the name of the field and the value to be set. The `onChange` function looks like this:

```
const handleChange = (e) => {
  formik.setFieldValue('myFile', e.target.files[0]);
};
```

The result of this is that the file is validated every time its value changes and when the form is submitted. Also, Formik's `handleSubmit` helper function is passed into the form element's `onSubmit` property. This will trigger Formik's own `onSubmit` function. 

Here's the complete code:

```
import React from "react";
import { useFormik } from "formik";
import * as Yup from 'yup';

const MyForm = () => {
  const validationRules = Yup.object().shape({
    myFile: Yup.mixed().required('required')
      .test('fileFormat', 'Only PDF files are allowed', value => {
        if (value) {
          const supportedFormats = ['pdf'];
          return supportedFormats.includes(value.name.split('.').pop());
        }
        return true;
      })
      .test('fileSize', 'File size must not be more than 3MB', 
      value => {
        if (value) {
          return value.size <= 3145728;
        }
        return true;
      }),
  })

  const formik = useFormik({
    initialValues: {
      myFile: ''
    },
    onSubmit: () => {
      console.log('Submitted')
    },
    validationSchema: validationRules,
  })

  const handleChange = (e) => {
    formik.setFieldValue('myFile', e.target.files[0]);
  };
  return (
    <form onSubmit={formik.handleSubmit}>
      <input type='file' name='myFile' accept='.pdf' onChange={handleChange}/>

      <div>{(formik.errors.myFile) ? <p style={{color: 'red'}}>{formik.errors.myFile}</p> : null}</div>
      <br/>
      <button type='submit'>Submit</button>
    </form>
  )
}

export default MyForm;
```

That's it! You can check out the code output on Stackblitz [here](https://stackblitz.com/edit/react-formik-yup-example-uhdg-2qq4v1?file=MyForm.js). Thank you for reading, and please let me know of your thoughts about this article.