Next JS – React Hook Form with Zod Complete

How to write a Front End and Back End (Server side validation). It is really important to provide both client-side and server-side validation. The code below shows how to implement a form that provides both using react hook form and Zod.

 

Next JS – React Hook Form with Zod Complete

Creation of React Hook Form [Front End Validation]

Just create a page at app/Second/page.tsx

‘use client’

import { useForm, SubmitHandler } from “react-hook-form”

import { zodResolver } from “@hookform/resolvers/zod”

import {SignupSchema, TSignUpSchema} from “@/lib/signupschema”

 

export default function second() {

 

const {

register,

handleSubmit,

setError,

formState:{errors},

} = useForm<TSignUpSchema>({

resolver:zodResolver(SignupSchema)

});

 

 

    const onSubmit = async (data: TSignUpSchema) => {

const result = await fetch(‘/api/signup’, {

method: ‘POST’,

headers: {

‘Content-Type’: ‘application/json’,

},

 

body: JSON.stringify(data),

 

});

 

const responseData = await result.json();

 

if(!responseData.success){

alert(‘The form has mistakes’)

}

 

if(responseData.errors){

const error = responseData.errors;

 

if(error.username){

setError(‘username’, {

message: error.username,

type:’server’,

})

}

else if(error.password){

setError(‘password’, {

message:error.password,

type:’server’,

})

 

}

else if(error.confirmpassword){

setError(‘confirmpassword’, {

message:error.confirmpassword,

type:’server’,

})

}

else{

alert(‘something  is wrong over here’)

}

}

return true;

}

 

return (

<div>

<h1 className=’text-2xl’>Second Registration Form</h1>

<form className=’max-w-sm mx-auto’ onSubmit = {handleSubmit(onSubmit)}>

<div className=’m-5′>

<input className=’bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500′ placeholder= “username” {…register(“username”)} />

{errors.username && <p className=’text-red-500′>{errors.username.message}</p>}

</div>

 

<div className=’m-5′>

<input className=’bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500′ placeholder= “password” {…register(“password”)} />

{errors.password && <p className=’text-red-500′>{errors.password.message}</p>}

</div>

 

<div className=’m-5′>

<input className=’bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500′ placeholder= “confirmpassword” {…register(“confirmpassword”)} />

{errors.confirmpassword && <p className=’text-red-500′>{errors.confirmpassword.message}</p>}

</div>

<div className=’m-5′>

<input className=’bg-black text-orange-200 w-auto p-2′ type=”submit” />

</div>

 

 

</form>

</div>

)

}

 

Zod and Schema

lib/signupschema.ts

 

import { z } from “zod”

 

export const SignupSchema = z.object({

username: z.string().min(6,{message:”Username must be atleast 6 characters”}),

password: z.string().min(6,{message:”Password must be atleast 6 characters”}),

confirmpassword: z.string().min(6, {message:”Password must be atleast 6 characters”}),

}).refine(data => data.password === data.confirmpassword, {

message: “Passwords do not match”,

path: [“confirmpassword”]

});

 

export type TSignUpSchema = z.infer<typeof SignupSchema>

 

Creation of API End Point [Back End Server-Side Validation]

/app/api/signup/route.ts

import {NextResponse} from ‘next/server’

import {SignupSchema} from “@/lib/signupschema”

 

export async function POST(request:Request){

 

const body: unknown = await request.json();

const result = SignupSchema.safeParse(body)

 

let zodErrors = {};

 

if(!result.success){

const listErrors= result.error.issues

listErrors.forEach((err) => {

zodErrors = {…zodErrors, [err.path[0]]:err.message}  // This will add new property into the zod Errors object

});

}

 

return NextResponse.json(

Object.keys(zodErrors).length > 0 ? {errors: zodErrors} :{success : true}

);

}