Validating file presence with YUP
Asked Answered
R

7

46

I'm using Yup to validate my form. In one of my form, I want to validate that one <input type="file" /> has a file.

I've tested this (and it's not working):

Yup.object().shape({
  file: Yup.object().shape({
    name: Yup.string().required()
}).required('File required')

I've the following error message in the console:

file must be a object type, but the final value was: null (cast from the value {}). If "null" is intended as an empty value be sure to mark the schema as .nullable()

Any idea?

Regine answered 20/9, 2018 at 14:8 Comment(1)
For peeps in the future, this issue here explains it: github.com/jaredpalmer/formik/issues/926Remainderman
F
33

Here is how I did it

import { object, string, mixed } from "yup"

const schema = object().shape({
  attachment: mixed().test("fileSize", "The file is too large", (value) => {
    if (!value.length) return true // attachment is optional
    return value[0].size <= 2000000
  }),
})
Freemasonry answered 31/7, 2020 at 20:22 Comment(0)
A
28

I solved it like this:

const schema = Yup.object().shape({
    file: Yup.mixed().required('File is required'),
})

It throws an error if you attempt to submit the form without a file, and the error goes away when you have a file.

Atheism answered 4/8, 2021 at 16:13 Comment(0)
D
10

The other answers definitely get it right. This is an alternative approach that I've grown accustomed to following.

const validationSchema = Yup.object().shape({
 [Form.File]: Yup.mixed()
  .test({
    message: 'Please provide a supported file type',
    test: (file, context) => {
      const isValid = ['png', 'pdf'].includes(getExtension(file?.name));
      if (!isValid) context?.createError();
      return isValid;
    }
  })
  .test({
    message: `File too big, can't exceed ${MAX_FILE_SIZE}`,
    test: (file) => {
      const isValid = file?.size < MAX_FILE_SIZE;
      return isValid;
    }
  })
});

getExtension is a custom utility to grab the ext type of a file. As Yup is usually utilized with Formik, the context argument will allow you to display an error message relevant to the test that you throw it in.

Using the useFormik hook, you'll be able to grab the error from it:

const formik = useFormik({
  validationSchema,
  initialValues,
  onSubmit
});

formik.errors[Form.File]
Daria answered 29/4, 2022 at 19:29 Comment(1)
This looks like something that would work for me. I need to validate audio file type and size as well as image file type and size.Anselmo
K
9

I know this is an old question, but I came across this same issue and tracked down what was happening.

When the validator runs, it's testing that the field type matches first. In this case, it checks to see if it's an object. Since it's not and the field is not marked as nullable, it fails the validation with a type error. Yup never gets to the point of running required() as it bails early.

So how do we adjust the message displayed when the type doesn't match and you don't want to use the nullable() method? I looked at the source and discovered that the message is generated here. So it's part of the locale object. Thus, we can resolve it like this.

import * as Yup from 'yup';
import { setLocale } from 'yup';

setLocale({
  mixed: {
    notType: '${path} is required',
  }
})

Yup.object().shape({
  file: Yup.object().shape({
  name: Yup.string().required()
}).label('File')

Now when the type is incorrect, your new message will display. The label() method allows you to set a nice display name for the field as the message will be used in any invalid type situations in the validators.

Another alternative is to write a custom validator specifically for this field, but that seems like overkill in this scenario.

Kenny answered 28/11, 2018 at 1:12 Comment(0)
D
0

This is what worked for me:

  file: yup.object().shape({
      name: yup.string().required('File Required')
  }).required()

  const [file, setFile] = useState(null)  

  const _test = (data) => {
      const _fromData = new FormData();
      _fromData.append('file', file);
  }  

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
    setValue('file', event.target.files[0].name);
    clearErrors('file');    
  } 

Then we add the following to the form which will remove the error on file select:

onSubmit={handleSubmit(_test)}

We can then display the error as follows:

{errors?.file?.name?.message}
Detruncate answered 30/8, 2023 at 7:48 Comment(0)
D
0

I struggled getting it to work with Yup.object().shape({...}). I kept getting error:

file must be a `object` type, but the final value was: `{}`.

So heres my solution sample code.

const FileSchema = Yup.mixed()
  .test('File is required', 'File ', (value: File) => value)
  .test('Size', 'File size must be less than 20kb', (value: File) => value && value?.size <= 20000)
Dianthe answered 11/3 at 18:23 Comment(0)
T
0

below code works to me for image validation:

images: Yup.array()
        .min(1, "At least one image is required")
        .of(
          Yup.mixed().test("fileType", "Unsupported file format", (value) =>
            ["image/jpeg", "image/png", "image/gif"].includes(value?.type)
          )
        )
        .required("Images are required"),
Traceetracer answered 24/6 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.