Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save AndersonNascimentoDosSantos/b3b8f760f3a431fe9e0ee440f5b0e2d4 to your computer and use it in GitHub Desktop.
Save AndersonNascimentoDosSantos/b3b8f760f3a431fe9e0ee440f5b0e2d4 to your computer and use it in GitHub Desktop.
Zod image validation
"use client";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"; //shadcn ui folder
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { BsImages, BsPaperclip } from "react-icons/bs";
import { IoSendOutline } from "react-icons/io5";
import * as z from "zod";
const MAX_FILE_SIZE = 1024 * 1024 * 5;
const ACCEPTED_IMAGE_MIME_TYPES = [
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
];
const ACCEPTED_IMAGE_TYPES = ["jpeg", "jpg", "png", "webp"];
const formSchema = z.object({
adImage: z
.any()
.refine((files) => {
return files?.[0]?.size <= MAX_FILE_SIZE;
}, `Max image size is 5MB.`)
.refine(
(files) => ACCEPTED_IMAGE_MIME_TYPES.includes(files?.[0]?.type),
"Only .jpg, .jpeg, .png and .webp formats are supported."
),
});
export type ContactFormData = z.infer<typeof formSchema>;
export default function Home() {
const [selectedImage, setSelectedImage] = useState<File | null>(null);
const form = useForm<ContactFormData>({
resolver: zodResolver(formSchema),
defaultValues: {
adImage: undefined,
},
});
const onSubmit = async (data: ContactFormData) => {
console.log(data);
};
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className={cn("flex md:flex-row w-[100%] gap-4 flex-col")}>
<div className="flex w-[100%] gap-4 flex-col ">
<FormLabel>your form title</FormLabel>
<div
className={`flex w-[100%] gap-4 p-4 rounded border border-neutral-200 flex-col items-center md:flex-row md:justify-between md:items-center`}
>
<div
className={`flex md:flex-[1] h-[fit-content] md:p-4 md:justify-between md:flex-row
`}
>
{selectedImage ? (
<div className="md:max-w-[200px]">
<img
src={URL.createObjectURL(selectedImage)}
alt="Selected"
/>
</div>
) : (
<div className="inline-flex items-center justify-between">
<div className="p-3 bg-slate-200 justify-center items-center flex">
<BsImages size={56} />
</div>
</div>
)}
</div>
<FormField
control={form.control}
name="adImage"
render={({ field }) => (
<FormItem>
<FormControl>
<Button size="lg" type="button">
<input
type="file"
className="hidden"
id="fileInput"
accept="image/*"
onBlur={field.onBlur}
name={field.name}
onChange={(e) => {
field.onChange(e.target.files);
setSelectedImage(e.target.files?.[0] || null);
}}
ref={field.ref}
/>
<label
htmlFor="fileInput"
className="bg-blue-500 hover:bg-blue-600 text-neutral-90 rounded-md cursor-pointer inline-flex items-center"
>
<BsPaperclip />
<span className="whitespace-nowrap">
choose your image
</span>
</label>
</Button>
</FormControl>
{/* <FormDescription>This is your public display email.</FormDescription> */}
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</div>
<div className={cn("flex w-[100%] gap-4 justify-end")}>
<div className="space-y-2">
<Button className="gap-1 py-4 px-4" type="submit">
<span>SUBMIT</span>
<IoSendOutline />
</Button>
</div>
</div>
</form>
</Form>
</main>
);
}
@AndersonNascimentoDosSantos
Copy link
Author

@rudy128
i tried to edit you code to a better reading but it did not work well,
all images you shared are private and i couldn't see any of then
can you share they again?

most of the difference i see in your code was this piece

const form = useForm<z.infer>({ resolver: zodResolver(formSchema), defaultValues: { productName: "", productDescription: "", productImage: undefined }, })

where you may received a error of "you're using a function/value as type" or something similar to it

Captura de tela de 2024-07-07 15-14-07

this is a option to infer directly into the useForm<>
Captura de tela de 2024-07-07 15-14-07

@Imanullah
Copy link

Thank you!

However, if I want to check if the file is uploaded in the first, the resolved error message would be "Max document size is 10MB." instead of "Document is required.".

Here's the code I'm using:

document: z
  .any()
  .refine(
    (files) => !files || files?.length === 0,
    "Document is required."
  )
  .refine((files) => {
    return files?.[0]?.size <= MAX_DOCUMENT_FILE_SIZE;
  }, `Max document size is 10MB.`)
  .refine(
    (files) => ACCEPTED_DOCUMENT_MIME_TYPES.includes(files?.[0]?.type),
    "Only .pdf format is supported."
  ),

here is for file required
.refine((files) => files?.length !== 0, 'Document is required')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment