Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validating file input #387

Closed
wassim opened this issue Apr 7, 2021 · 70 comments
Closed

Validating file input #387

wassim opened this issue Apr 7, 2021 · 70 comments

Comments

@wassim
Copy link

wassim commented Apr 7, 2021

Coming from Yup. I'm trying to validate a required file input with no success:

file: z.any().refine(val => val.length > 0, "File is required")

Any tips?

@wassim wassim changed the title Validating input="file" Validating file input Apr 7, 2021
@vensauro
Copy link

vensauro commented Apr 8, 2021

i guess that you can check if is a instace of File

@colinhacks
Copy link
Owner

@wassim is this a File instance? If so just use z.instanceof(File). Though there's no length property on the File class: https://developer.mozilla.org/en-US/docs/Web/API/File

@niklasgrewe
Copy link

niklasgrewe commented May 3, 2021

@colinhacks thanks for your answer. I am also looking for a solution. Can i also check if the File MIME-Type is for e.g. an image/*. How can i do that with zod?

and when i try to use:

file: z.instanceof(File)

i get the error:

ReferenceError: File is not defined

@ProxyJoshua
Copy link

image
I need to validate this, but I don't know how can I match the type using FileList type

@colinhacks
Copy link
Owner

You really shouldn't use toZod, it's very fragile and not supported.. It's also totally unnecessary here because you're not trying to define recursive schemas (which is the only scenario where it sorta comes in handy). Just get rid of the : toZod<blahblah> annotations and this will work.

@colinhacks
Copy link
Owner

@niklasgrewe File is a built-in class that's only defined on the browser, not in Node.js environments. If you're using a Node.js library to handle file uploads, use the File class provided by that library (it may not be called "File", it depends on the library).

If you're writing code that will run in the browser, you need to tell TypeScript to include browser-specific classes like File. Add "dom" to your "lib" array in tsconfig.json:

{
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom" // <- add this
    ],
    // ... other stuff
  }
}

@statusunknown418
Copy link

@niklasgrewe File is a built-in class that's only defined on the browser, not in Node.js environments. If you're using a Node.js library to handle file uploads, use the File class provided by that library (it may not be called "File", it depends on the library).

If you're writing code that will run in the browser, you need to tell TypeScript to include browser-specific classes like File. Add "dom" to your "lib" array in tsconfig.json:

{
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom" // <- add this
    ],
    // ... other stuff
  }
}

Hey this still is not fixing the referenceError File is not defined error. Using next's 12.1.6

Screen Shot 2022-06-28 at 11 16 28

@CoreyGumbs
Copy link

@niklasgrewe File is a built-in class that's only defined on the browser, not in Node.js environments. If you're using a Node.js library to handle file uploads, use the File class provided by that library (it may not be called "File", it depends on the library).

If you're writing code that will run in the browser, you need to tell TypeScript to include browser-specific classes like File. Add "dom" to your "lib" array in tsconfig.json:

{
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom" // <- add this
    ],
    // ... other stuff
  }
}

I am getting the same error as @AlvaroAquijeDiaz . "dom" is already in the config and it's not working.

FYI: I am currently writing a schema using nextjs and react-hook-forms.

ReferenceError: File is not defined

@statusunknown418
Copy link

Any ideas?

@d4vvv
Copy link

d4vvv commented Jul 4, 2022

@niklasgrewe File is a built-in class that's only defined on the browser, not in Node.js environments. If you're using a Node.js library to handle file uploads, use the File class provided by that library (it may not be called "File", it depends on the library).
If you're writing code that will run in the browser, you need to tell TypeScript to include browser-specific classes like File. Add "dom" to your "lib" array in tsconfig.json:

{
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom" // <- add this
    ],
    // ... other stuff
  }
}

I am getting the same error as @AlvaroAquijeDiaz . "dom" is already in the config and it's not working.

FYI: I am currently writing a schema using nextjs and react-hook-forms.

ReferenceError: File is not defined

I am experiencing the same issue.

@CoreyGumbs
Copy link

CoreyGumbs commented Jul 11, 2022

Any ideas?

@AlvaroAquijeDiaz @niklasgrewe @colinhacks @d4vvv

So I was refactoring and doing some code cleanup on my forms component and I think I accidentally found a temporary solution. I say temporary because it doesn't completely address the problem.

I added z.instanceof(File) into my schema and then put the schema in a separate file ts file.

When I imported it into my forms component, the ReferenceError disappeared. I tried on both Chrome and Firefox and it worked.

See if that works for you.

@statusunknown418
Copy link

@CoreyGumbs I'll definitely try this, thanks

@HARAJLI98
Copy link

@CoreyGumbs Tried your solution but no luck on my end. Moved the schema into its own file like you said but got the same error. @AlvaroAquijeDiaz Did it work for you?

@statusunknown418
Copy link

@HARAJLI98 yep still getting errors, I think we'll just have to use the z. any

@CoreyGumbs
Copy link

@HARAJLI98 @AlvaroAquijeDiaz

Sorry for the late reply, just getting back to my project. So I don't know what I did, or what happened, and wasn't able to reproduce the result, but I too am getting an error when trying this on a new form Im making.

I created an object with the properties from the FILE/FILELIST obj just so I could move forward and test my upload on my form:

upload_files: z.object({ name: z.string({ required_error: "Please upload a valid file type. (MP3/MP4, JPG, JPEG, PNG)", }), lastModified: z.number(), size: z.number(), type: z.string(), }),

this is probably not the best solution but it did get rid of the type of errors I was getting, even with .any()

@zanzlender
Copy link

zanzlender commented Jul 21, 2022

Putting this here if anyone still has this problem. The solution that worked for me was this one:

const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const RegistrationSchema = z.object({
  profileImage: z
    .any()
    .refine((files) => files?.length == 1, "Image is required.")
    .refine((files) => files?.[0]?.size <= MAX_FILE_SIZE, `Max file size is 5MB.`)
    .refine(
      (files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
      ".jpg, .jpeg, .png and .webp files are accepted."
    ),
});

No image provided
image

Too big
image

Wrong type
image

All good ✔
image

Then finally, in the data object we get
image

@hydego17
Copy link

hydego17 commented Jul 29, 2022

@HARAJLI98 yep still getting errors, I think we'll just have to use the z. any

Hey I think this will solve your problem, I've tried it myself and it works
blitz-js/blitz#2292 (reply in thread)

@bryanltobing
Copy link

bryanltobing commented Aug 12, 2022

https://gist.github.com/bryantobing12/443f2c417b4ed537284be7cf55bf10ad, this is what works for me

The previous link is broken since I changed my username.
https://gist.github.com/bryanltobing/443f2c417b4ed537284be7cf55bf10ad

@officialtbabs
Copy link

officialtbabs commented Aug 26, 2022

Putting this here if anyone still has this problem. The solution that worked for me was this one:

const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const RegistrationSchema = z.object({
  profileImage: z
    .any()
    .refine((files) => files?.length === 0, "Image is required.") // if no file files?.length === 0, if file files?.length === 1
    .refine((files) => files?.[0]?.size >= MAX_FILE_SIZE, `Max file size is 5MB.`) // this should be greater than or equals (>=) not less that or equals (<=)
    .refine(
      (files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
      ".jpg, .jpeg, .png and .webp files are accepted."
    ),
});

No image provided image

Too big image // It will also accept this file too so looks for a file bigger that 5MB

Wrong type image

All good ✔ image

Then finally, in the data object we get image

@hassannaveed24
Copy link

Coming from Yup. I'm trying to validate a required file input with no success:

file: z.any().refine(val => val.length > 0, "File is required")

Any tips?

I resolved this in my scenario
identity: zod.any().refine(file => file, "Please upload ID"),

@f0-x
Copy link

f0-x commented Oct 11, 2022

@officialtbabs Could you please share with us the code where you integrated the form with the schemas defined above ? filetype & filesize validations are getting skipped for me.

@ghost
Copy link

ghost commented Dec 9, 2022

zod.any().refine(file => file, "Please upload ID")

This solution worked for me!

@QzCurious
Copy link

QzCurious commented Dec 15, 2022

To make it more type safe. I do

z.any() as ZodType<File>

Hope you find it useful as well.

UPDATE!!
zod recently document the feature for custom schema. (seem like this method already being there for a while)

z.custom<File>()

@just-Bri
Copy link

just-Bri commented Dec 22, 2022

UPDATE!! zod recently document the feature for custom schema. (seem like this method already being there for a while)

z.custom<File>()

Just saved me so much time!
If anyone else ends up here trying to figure out how to use swagger => ts generated types in zod this is it!

export type TRecipeFile = components['schemas']['RecipeFile'];

const BlahSchema = z.object({
  file: z.custom<TRecipeFile>(),
});

TRecipeFile is output via npx openapi-typescript

@ayush-seth
Copy link

To make it more type safe. I do

z.any() as ZodType<File>

Hope you find it useful as well.

UPDATE!! zod recently document the feature for custom schema. (seem like this method already being there for a while)

z.custom<File>()

This will bypass typechecking. To make it typesafe, provide a validation function like this:

z.custom<File>((v) => v instanceof File)

@fdFloresMarquez
Copy link

This is how i managed to do it with an array of files.

const formSchema= z.object({
  image: z
    .array(z.custom<File>())
    .refine(
      (files) => {
        // Check if all items in the array are instances of the File object
        return files.every((file) => file instanceof File);
      },
      {
        // If the refinement fails, throw an error with this message
        message: 'Expected a file',
      },
    )
    .refine(
      (files) => files.every((file) => file.size <= MAX_FILE_SIZE),
      `File size should be less than 2mb.`,
    )
    .refine(
      (files) => files.every((file) => ACCEPTED_IMAGE_TYPES.includes(file.type)),
      'Only these types are allowed .jpg, .jpeg, .png and .webp',
    ),
})

@danilo-vieira
Copy link

Hey everyone, you can use z.instanceof(File) to validate and make it typesafe at same time. Try:

const schema = z.object({
  file: z.instanceof(File),
})

It will infer the type and ensure that it is instance of File. Check it out ☕️🗿

@Kavindu-Wijesekara
Copy link

@PratikDev hey i have the same issue and i made a demo.
Zod + Shadcn form demo

Instead of passing the field prop in your file type input, pass the fileRef we get from form.register("file")

I changed it from

 <Input type="file" accept="application/pdf" {...field} />

to

 <Input type="file" accept="application/pdf" {...fileRef} />

fileRef is coming from

const fileRef = form.register('file', { required: true });

also made the default value of file from an empty string to undefined

Edited Demo

@PratikDev i have tried edited demo. now its stuck on this refine 😕

refine((file) => file?.type === 'application/pdf', 'Must be a PDF.')

@PratikDev
Copy link

@Kavindu-Wijesekara it's because file?.type doesn't exist. file is a type of FileList. so you need to do file[0]?.type. The same goes for size. I made the changes. Also removed unused schema to check if the form was submitted successfully. You can check the edited demo

@Kavindu-Wijesekara
Copy link

@Kavindu-Wijesekara it's because file?.type doesn't exist. file is a type of FileList. so you need to do file[0]?.type. The same goes for size. I made the changes. Also removed unused schema to check if the form was submitted successfully. You can check the edited demo

@PratikDev Oh i didn't see that. Anyway thank you very much. Now it's working perfectly 👌

@PratikDev
Copy link

PratikDev commented Oct 23, 2023

Reason and Solution

Hey everyone. I just found it. Using z.instanceof(File) or anything similar throws File is not defined error, because the File class is only available on node-v20.0.0 or upper (related PR). so if your node is lower than this, and you're trying to run the code on the node environment instead of the browser, it'll throw the File is not defined error. The same goes with FileList.
So the solution would be to upgrade your node version to the latest one. and if you cannot afford that, u can try using the @web-std/file library and use the File class they provide

Edit

I tried using file instanceof File where File is coming from @web-std/file, and seems it returns false even if file is actually a file. So most probably z.instanceof(File) will also behave the same. might be an issue from @web-std/file. So the only solution left for now is to upgrade your node to v-20.0.0 or upper

@babakfp
Copy link

babakfp commented Dec 4, 2023

@PratikDev

Hi

console.log(formData.get("videos") instanceof File) // Works
console.log(formData.getAll("videos") instanceof FileList) // Doesn't work

Did you get FileList working yourself?

@PratikDev
Copy link

@PratikDev

Hi

console.log(formData.get("videos") instanceof File) // Works
console.log(formData.getAll("videos") instanceof FileList) // Doesn't work

Did you get FileList working yourself?

i'm not sure if FileList is available in NodeJS. i haven't tried it. In my project, I lastly moved file validation out of the Zod schema and did it manually. tho we have some workarounds for it, but it was making it too difficult to maintain as the project grows IMO

@qgatssdev
Copy link

If anyone's using z.instanceof approach and having a hard time giving a custom error message to the validation. This approach worked for me.

const ACCEPTED_FILE_TYPES = ['application/json']

z.custom<File>(val => val instanceof File, 'Please upload a file')
  .refine(
    file => ACCEPTED_FILE_TYPES.includes(file.type),
    { message: 'Please choose .json format files only' }
  ),

This works, thanks!

@abdessamadely
Copy link

abdessamadely commented Jan 6, 2024

From looking into this issue, I see that there's no implementation of the File class for NodeJs, and because the File extends Blob we can use z.instanceof(Blob) to validate it.

For TypeScript If you enable the dom lib File type will be present, so to make Typescript happy we can use as Type

Example for Next.js users:

const formData = await request.formData()
const alt = z.string().min(3).parse(formData.get('alt'))
const file = z.instanceof(Blob).parse(formData.get('file')) as File

console.log(file.name)

@alaa-m1
Copy link

alaa-m1 commented Jan 31, 2024

This is how to handle single or multiple files. 👌
The following example is for images with a maximum size of 4MB :

const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpg", "image/jpeg"];
const MAX_IMAGE_SIZE = 4; //In MegaBytes

const sizeInMB = (sizeInBytes: number, decimalsNum = 2) => {
  const result = sizeInBytes / (1024 * 1024);
  return +result.toFixed(decimalsNum);
};

const UserGeneralInfoSchema = z.object({
  profileImage: z
    .custom<FileList>()
    .refine((files) => {
      return Array.from(files ?? []).length !== 0;
    }, "Image is required")
    .refine((files) => {
      return Array.from(files ?? []).every(
        (file) => sizeInMB(file.size) <= MAX_IMAGE_SIZE
      );
    }, `The maximum image size is ${MAX_IMAGE_SIZE}MB`)
    .refine((files) => {
      return Array.from(files ?? []).every((file) =>
        ACCEPTED_IMAGE_TYPES.includes(file.type)
      );
    }, "File type is not supported"),
});

@Csutkas
Copy link

Csutkas commented Mar 31, 2024

For me import File from "buffer" helped, here is my snippet:

import { File } from "buffer";

const fileSchema = z.instanceof(File, { message: "Required" });
const imageSchema = fileSchema.refine(
  (file) => file.size === 0 || file.type.startsWith("image/")
);

const addScheme = z.object({
  name: z.string().min(1),
  file: fileSchema.refine(
    (file) => file.size > 0,
    "File size was 0, please upload a proper file!"
  ),
  image: imageSchema.refine(
    (file) => file.size > 0,
    "File size was 0, please upload a proper file!"
  ),
}); 

@dannycallaghan
Copy link

For me import File from "buffer" helped, here is my snippet:

import { File } from "buffer";

const fileSchema = z.instanceof(File, { message: "Required" });
const imageSchema = fileSchema.refine(
  (file) => file.size === 0 || file.type.startsWith("image/")
);

const addScheme = z.object({
  name: z.string().min(1),
  file: fileSchema.refine(
    (file) => file.size > 0,
    "File size was 0, please upload a proper file!"
  ),
  image: imageSchema.refine(
    (file) => file.size > 0,
    "File size was 0, please upload a proper file!"
  ),
}); 

This worked for me, thanks.

@josh-i386g
Copy link

some quick maths i use

2 ** 18 = 256 MB
2 ** 19 = 512 MB
2 ** 20 = 1 MB
2 ** 21 = 2 MB
2 ** 22 = 4 MB
2 ** 23 = 8 MB
2 ** 24 = 16 MB

@jonra1993
Copy link

jonra1993 commented Apr 27, 2024

Thanks to the other responses in this thread. This worked for me to collect multiple files.

const MAX_FILE_SIZE = 5000000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const schema = zod.object({
  image: zod
    .instanceof(FileList, { message: "Required" })
    .refine((file) => file?.length > 0, 'A file is required.')
    .refine((files) => {
      for (let i = 0; i < files.length; i++) {
        console.log(i, files[i])
        if (files[i].type in ACCEPTED_IMAGE_TYPES) {
          if (!ACCEPTED_IMAGE_TYPES.includes(files[i].type)) return false; // Check if it's an accepted image type
        }
      }
      return true;
    }, 'Must be a valid image.')
    .refine((files) => {
      for (let i = 0; i < files.length; i++) {
        if (files[i].size > MAX_FILE_SIZE) return false; // Check if size exceeds max size
      }
      return true;
    }, 'Max size reached.')
});

.......

              <Controller
                control={control}
                name="image"
                render={({ field: { value, onChange, ...field } }) => (
                  <FormControl error={Boolean(errors.image)} fullWidth>
                    <InputLabel required>Superpower card image</InputLabel>
                    <input
                      type="file"
                      accept={ACCEPTED_IMAGE_TYPES.join(', ')}
                      multiple
                      onChange={(event) => onChange(event?.target?.files ?? "")}
                      {...field}                      
                    />
                    
                    {errors?.image ? <FormHelperText>{errors.image.message}</FormHelperText> : null}
                  </FormControl>
                )}

@fzn0x
Copy link

fzn0x commented May 6, 2024

It is so complicated to only validate multiple files, is there a way to simplify this?

@fzn0x
Copy link

fzn0x commented May 6, 2024

Becareful of using .custom<FileList>() and Array validation, looks like it's not works when you upload one file.

@fzn0x
Copy link

fzn0x commented May 6, 2024

FileList didn't work as expected for me, but using Blob as an alternative was working fine.

export const PostUploadFileSchema = z
  .object({
    "image[]": z
      .custom<Blob>()
      .refine(
        (blob) => {
          return blob.size <= MAX_FILE_SIZE;
        },
        {
          message: `More than ${MAX_FILE_SIZE} are not allowed`,
        }
      )
      .refine((blob) => ACCEPTED_IMAGE_TYPES.includes(blob?.type), {
        message: "Only .jpg, .jpeg, .png and .webp formats are supported.",
      }),
  })
  .openapi("Post Upload File");
  
 // To convert Blob to File
 for (let image of images) {
  image = new File([image], image.name, {
    type: image.type,
  });

  if (image instanceof File) {
    await uploadFile(image);
  }
}

@buraxta
Copy link

buraxta commented Jun 11, 2024

@HARAJLI98 yep still getting errors, I think we'll just have to use the z. any

didn't know that "any" could solve my problem, thank you 👍

@Saiguna7
Copy link

Saiguna7 commented Jul 8, 2024

Putting this here if anyone still has this problem. The solution that worked for me was this one:

const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const RegistrationSchema = z.object({
  profileImage: z
    .any()
    .refine((files) => files?.length == 1, "Image is required.")
    .refine((files) => files?.[0]?.size <= MAX_FILE_SIZE, `Max file size is 5MB.`)
    .refine(
      (files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
      ".jpg, .jpeg, .png and .webp files are accepted."
    ),
});

No image provided image

Too big image

Wrong type image

All good ✔ image

Then finally, in the data object we get image
const MAX_FILE_SIZE = 510241024
const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
CertificateFile: z.any().optional()
.refine(file => file.length == 1 ? ACCEPTED_FILE_TYPES.includes(file?.[0]?.type) ? true : false : true, 'Invalid file. choose either JPEG ,jpg , webp PNG image')
.refine(file => file.length == 1 ? file[0]?.size <= MAX_FILE_SIZE ? true : false : true, 'Max file size allowed is 5MB.'),
})

@Saiguna7
Copy link

Saiguna7 commented Jul 8, 2024

`const MAX_FILE_SIZE = 510241024
const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
export const PostUploadFileSchema =z.object({
images:z.any().z.optional().refine(file => file.length == 1 ? ACCEPTED_FILE_TYPES.includes(file?.[0]?.type) ? true : false : true, 'Invalid file. choose either JPEG ,jpg , webp PNG image')
.refine(file => file.length == 1 ? file[0]?.size <= MAX_FILE_SIZE ? true : false : true, 'Max file size allowed is 5MB.'),
})
use this

`

@ram0973
Copy link

ram0973 commented Aug 19, 2024

If image is optional:

avatar: z.custom<File | undefined>()
      .refine(file => (!file || file?.size <= MAX_FILE_SIZE)!,
        {message: "Max image size is 5MB."})
      .refine(file => (!file || ACCEPTED_IMAGE_TYPES.includes(file?.type))!,
        ".jpg, .jpeg, .png, .webp, files are accepted only"),

@SoroMoise
Copy link

@wassim is this a File instance? If so just use z.instanceof(File). Though there's no length property on the File class: https://developer.mozilla.org/en-US/docs/Web/API/File

When i do that, i have this error : { membersFile: [ 'Input not instance of File2' ] }

@rifkyniyas
Copy link

If anyone is working with shadcn-ui form components and is trying to validate the schema via zod, you can refer the following article.

TLDR; if you are working on client components in next.js change your schema to

const formSchema = z.object({
  file: z.instanceof(FileList).optional(),
});

When defining the form

const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
});
const fileRef = form.register("file");

Then later when rendering the component

<Input type="file" placeholder="shadcn" {...fileRef} />

@Saiguna7
Copy link

Saiguna7 commented Sep 4, 2024

If anyone is working with shadcn-ui form components and is trying to validate the schema via zod, you can refer the following article.

TLDR; if you are working on client components in next.js change your schema to

const formSchema = z.object({
  file: z.instanceof(FileList).optional(),
});

When defining the form

const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
});
const fileRef = form.register("file");

Then later when rendering the component

<Input type="file" placeholder="shadcn" {...fileRef} />

so want to be optional ?

const MAX_FILE_SIZE = 5*1024*1024
const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
export const PostUploadFileSchema =z.object({
images:z.any().z.optional().refine(file => file.length == 1 ? ACCEPTED_FILE_TYPES.includes(file?.[0]?.type) ? true : false : true, 'Invalid file. choose either JPEG ,jpg , webp PNG image')
.refine(file => file.length == 1 ? file[0]?.size <= MAX_FILE_SIZE ? true : false : true, 'Max file size allowed is 5MB.'),
})

if want to limit the size 510241024 is 5mb if you don't want to limit the size remove that one

const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
export const PostUploadFileSchema =z.object({
images:z.any().z.optional().refine(file => file.length == 1 ? ACCEPTED_FILE_TYPES.includes(file?.[0]?.type) ? true : false : true, 'Invalid file. choose either JPEG ,jpg , webp PNG image')

})

###so you want optional
use the above one

use Shadcn ui form 
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
  FormDescription,
} from "@/components/ui/form";
<Form {...form}>
<form >
 <FormField
          control={form.control}
          name="name"
          render={({ field }) => (
            <FormItem>
              <FormLabel>File</FormLabel>
              <FormControl>
             <Input type="file" placeholder="shadcn" {...field } />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

</form>
</Form>

it will work
if you use const formSchema = z.object({
file: z.instanceof(FileList).optional(),
});
it will give error message even though it is optional if we leave that part empty

@universse
Copy link

universse commented Sep 22, 2024

here's an alternative to handle single or multiple files on both browser and server:

const Schema= z.object({
  files: z
    .preprocess(
      (value) => (Array.isArray(value) ? value : [value]),
      z.array(z.instanceof(File))
    )
    // ...
});

@Fiewor
Copy link

Fiewor commented Sep 25, 2024

If anyone is working with shadcn-ui form components and is trying to validate the schema via zod, you can refer the following article.

TLDR; if you are working on client components in next.js change your schema to

const formSchema = z.object({
  file: z.instanceof(FileList).optional(),
});

When defining the form

const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
});
const fileRef = form.register("file");

Then later when rendering the component

<Input type="file" placeholder="shadcn" {...fileRef} />

This gives me the following error when I deploy on Vercel.

Error occurred prerendering page "/dashboard/files/upload". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: FileList is not defined

@Fiewor
Copy link

Fiewor commented Sep 25, 2024

If anyone is working with shadcn-ui form components and is trying to validate the schema via zod, you can refer the following article.
TLDR; if you are working on client components in next.js change your schema to

const formSchema = z.object({
  file: z.instanceof(FileList).optional(),
});

When defining the form

const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
});
const fileRef = form.register("file");

Then later when rendering the component

<Input type="file" placeholder="shadcn" {...fileRef} />

This gives me the following error when I deploy on Vercel.

Error occurred prerendering page "/dashboard/files/upload". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: FileList is not defined

Fixed it with this from the originally referenced issue.

file: typeof window === 'undefined' ? z.any() : z.instanceof(FileList)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests