DEV Community

Aashutosh Poudel
Aashutosh Poudel

Posted on

File uploads and validation with React and Formik

Formik doesn't provide a way to handle file uploads easily. I found a decent solution looking at various github issues and stackoverflow discussions.

Here is a codesandbox showing how to validate the size of the files as well as their MIME types.
Link: https://codesandbox.io/s/hardcore-ully-tdk4iu?file=/src/App.js

The basic idea behind working with files in Formik is to make the file input component uncontrolled and access the selected files with a ref. More info here: https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

Step 1: We create a ref inside the form and pass that ref to the file input component.

export default function App() {
  const initialValues = {
    files: ""
  };
  const fileRef = useRef(null);
  return (
    <Formik
      initialValues={initialValues}
      validationSchema={Yup.object({
        files: Yup.mixed()
          {/* ... */}
      })}
      onSubmit={(values) => {
        {/* ... */}
      }}
    >
      <Form>
        <FileUpload name="files" fileRef={fileRef} />
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Inside the <FileUpload> component we use the passed ref as follows:

const FileUpload = ({ fileRef, ...props }) => {
  const [field, meta] = useField(props);
  return (
    <div>
      <label htmlFor="files">Choose files</label>{" "}
      <input ref={fileRef} multiple={true} type="file" {...field} />
      {meta.touched && meta.error ? (
        <div style={{ color: "red" }}>{meta.error}</div>
      ) : null}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Step 3: We validate the size of files uploaded and the MIME type of the file as follows:
Note: we are accessing the files directly using ref.

validationSchema={Yup.object({
        files: Yup.mixed()
          .test("is-file-too-big", "File exceeds 10MB", () => {
            let valid = true;
            const files = fileRef?.current?.files;
            if (files) {
              const fileArr = Array.from(files);
              fileArr.forEach((file) => {
                const size = file.size / 1024 / 1024;
                if (size > 10) {
                  valid = false;
                }
              });
            }
            return valid;
          })
          .test(
            "is-file-of-correct-type",
            "File is not of supported type",
            () => {
              let valid = true;
              const files = fileRef?.current?.files;
              if (files) {
                const fileArr = Array.from(files);
                fileArr.forEach((file) => {
                  const type = file.type.split("/")[1];
                  const validTypes = [
                    "zip",
                    "xml",
                    "xhtml+xml",
                    "plain",
                    "svg+xml",
                    "rtf",
                    "pdf",
                    "jpeg",
                    "png",
                    "jpg",
                    "ogg",
                    "json",
                    "html",
                    "gif",
                    "csv"
                  ];
                  if (!validTypes.includes(type)) {
                    valid = false;
                  }
                });
              }
              return valid;
            }
          )
      })}
Enter fullscreen mode Exit fullscreen mode

References:

  1. https://github.com/jaredpalmer/formik/issues/926
  2. https://stackoverflow.com/a/55570763/7358595

Top comments (0)