Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 840

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 62k
Author
Asked: November 25, 20242024-11-25T02:35:08+00:00 2024-11-25T02:35:08+00:00

NextJs image upload with zod validation

  • 62k

Hello everyone πŸ‘‹ Today I wanted to share one of my recent struggles: uploading files with validation in Next.js using useForm. There are many pitfalls for beginners, so I hope this tutorial will help 😊.

Firstly, I will assume you have a basic Next.js setup ready and you know the basics. So, let’s get right into it.

Install zod and zod-form-data. I'll be using npm for that. The command is:

npm i zod zod-form-data

Now, let's create our page. I will also be using the shadcn library for this tutorial. If you want to use it too, run these commands:

npx shadcn-ui@latest init npx shadcn-ui@latest add input npx shadcn-ui@latest add button npx shadcn-ui@latest add form 
Enter fullscreen mode Exit fullscreen mode

1) Create a file for your page with the .tsx extension. Add the “use client” directive at the top and import the necessary dependencies.

"use client";  import { Button } from "@/components/ui/button"; import {   Form,   FormControl,   FormField,   FormItem,   FormLabel,   FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { userFormSchema } from "@/types/formSchema"; import { updateUser } from "@/lib/auth";  
Enter fullscreen mode Exit fullscreen mode

2) Now, we will create our default export and create the necessary components inside.

export default function Page() {   const form = useForm<z.infer<typeof userFormSchema>>({     resolver: zodResolver(userFormSchema),     defaultValues: {       login: "",       email: "",       password: "",     },   });    function onSubmit(values: z.infer<typeof userFormSchema>) {     const formData = new FormData();     values.login && formData.append("login", values.login);     values.email && formData.append("email", values.email);     values.password && formData.append("password", values.password);     values.profileImage && formData.append("profileImage", values.profileImage);      updateUser(formData);   }    return (     <Form {...form}>       <form         onSubmit={form.handleSubmit(onSubmit)}         className="grid gap-4 lg:grid-cols-2 p-4 bg-black rounded-lg"       >         <FormField           control={form.control}           name="profileImage"           render={({ field: { value, onChange, ...fieldProps } }) => (             <FormItem>               <FormLabel>Profile picture</FormLabel>               <FormControl>                 <Input                   className="bg-neutral-900"                   type="file"                   {...fieldProps}                   accept="image/png, image/jpeg, image/jpg"                   onChange={(event) =>                     onChange(event.target.files && event.target.files[0])                   }                 />               </FormControl>               <FormMessage />             </FormItem>           )}         />         <FormField           control={form.control}           name="login"           render={({ field }) => (             <FormItem>               <FormLabel>Login</FormLabel>               <FormControl>                 <Input                   className="bg-neutral-900"                   placeholder="Enter login"                   {...field}                 />               </FormControl>               <FormMessage />             </FormItem>           )}         />         <FormField           control={form.control}           name="email"           render={({ field }) => (             <FormItem>               <FormLabel>Email</FormLabel>               <FormControl>                 <Input                   className="bg-neutral-900"                   placeholder="Enter email"                   {...field}                 />               </FormControl>               <FormMessage />             </FormItem>           )}         />         <FormField           control={form.control}           name="password"           render={({ field }) => (             <FormItem>               <FormLabel>Password</FormLabel>               <FormControl>                 <Input                   className="bg-neutral-900"                   placeholder="Enter password"                   {...field}                 />               </FormControl>               <FormMessage />             </FormItem>           )}         />         <Button type="submit" className=" text-black w-full lg:col-span-2">           Submit         </Button>       </form>     </Form>   ); }  
Enter fullscreen mode Exit fullscreen mode

Now, I want to explain some of this code. Look at the first FormField. We are destructuring from render because we need to parse the file.

Now, look at the onSubmit function. We are creating formData and appending values because we can't parse non-plain objects to the server action, and the file is making it non-plain.

Form is just a React useForm() that we are assigning userFormSchema to. We are using zodResolver as the resolver for client validation and setting default values.

3) Now we will create userFormSchema. The name can be anything, but remember to use it correctly. You can create it in the same file or, like me, import it.

import { z } from "zod"; import { zfd } from "zod-form-data";  const userFormSchema = zfd.formData({   login: z       .string()       .min(1, {         message: "Login can't be empty.",       })    password: z       .string()       .min(8, {         message: "Password must be mix 8 characters long.",       })       .max(20, {         message: "Password must be max 20 characters long.",       })       .regex(         /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]{8,}$/,         "Password must include one small letter, one uppercase letter and one number"       )    email: z       .string()       .email({         message: "Email is not in the correct format",       })       .min(1, {         message: "Email can't be empty.",       })    profileImage: zfd     .file()     .refine((file) => file.size < 5000000, {       message: "File can't be bigger than 5MB.",     })     .refine(       (file) => ["image/jpeg", "image/png", "image/jpg"].includes(file.type),       {         message: "File format must be either jpg, jpeg lub png.",       }     ) });  export { userFormSchema };  
Enter fullscreen mode Exit fullscreen mode

We are using zfd from zod-form-data for the file field and setting our validation. We are using refine for file validation (size and format).

4) Now, let's create our server action. The file must have the .ts extension and the 'use server' directive at the top.

'use server';  import { userFormSchema } from "@/types/formSchema"; import fs from "node:fs/promises";  const updateUser = async (data: FormData) => {   const safeData = userFormSchema.safeParse(data);   if (!safeData.success) throw new Error("Invalid data");   const { login, email, password, profileImage } = safeData.data;    try {     const arrayBuffer = await profileImage.arrayBuffer();     const buffer = new Uint8Array(arrayBuffer);     const filePath = `./public/uploads/${Date.now()}_${profileImage.name}`;      await fs.writeFile(filePath, buffer);      // Also here you cand do something with the rest of the data   } catch (error: any) {     throw new Error("Something went wrong");   } };  export { updateUser }; 
Enter fullscreen mode Exit fullscreen mode

I haven't done anything with the rest of the data; this was only to show you how to combine both zod and zfd. We are using fs to write the file to our chosen location.

That is all for this guide. I hope you enjoyed it 😊 and learned a lot 🧠. Have a nice day β˜€οΈ. Love you all πŸ’–. If you have any feedback, feel free to leave it in the comments and please share this tutorial.

nextjsreactwebdevzod
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.