Matheus Almeida's iconMatheusAlmeida

Forms with React Hook Form and Zod

First, we need to install the necessary dependencies:

yarn add react-hook-form @hookform/resolvers zod
yarn add react-hook-form @hookform/resolvers zod

Now, we import them:

import { zodResolver } from '@hookform/resolvers/zod'
import { SubmitHandler, useForm } from 'react-hook-form'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { SubmitHandler, useForm } from 'react-hook-form'
import { z } from 'zod'

We use z.object()z.object() to create a new schema. The schema contains the fields that make up our form.

const formSchema = z
  .object({
    name: z.string().min(1, 'Name is required').max(100),
    email: z.string().min(1, 'Email is required').email('Invalid email'),
    password: z
      .string()
      .min(1, 'Password is required')
      .min(8, 'Password must have at least 8 characters'),
    confirmPassword: z.string().min(1, 'Password confirmation is required'),
    terms: z.literal(true, {
      errorMap: () => ({ message: 'You must accept the terms and conditions' }),
    }),
  })
const formSchema = z
  .object({
    name: z.string().min(1, 'Name is required').max(100),
    email: z.string().min(1, 'Email is required').email('Invalid email'),
    password: z
      .string()
      .min(1, 'Password is required')
      .min(8, 'Password must have at least 8 characters'),
    confirmPassword: z.string().min(1, 'Password confirmation is required'),
    terms: z.literal(true, {
      errorMap: () => ({ message: 'You must accept the terms and conditions' }),
    }),
  })

To validate password confirmation, we use the refine()refine() method, which accepts two parameters: a callback and an object.

const formSchema = z
  .object({
 
    // Rest of the code
    ...
  }).refine((data) => data.password === data.confirmPassword, {
    path: ['confirmPassword'],
    message: 'Passwords do not match',
  })
const formSchema = z
  .object({
 
    // Rest of the code
    ...
  }).refine((data) => data.password === data.confirmPassword, {
    path: ['confirmPassword'],
    message: 'Passwords do not match',
  })

We use z.inferz.infer to extract the form’s type:

type FormSchemaType = z.infer<typeof formSchema>
type FormSchemaType = z.infer<typeof formSchema>

Finally, inside the form component, we implement React Hook Form passing zodResolver()zodResolver() to resolverresolver. zodResolver()zodResolver() receives the previously created schema as a parameter.

export const Form = () => {
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<FormSchemaType>({
 
    // Making use of zodResolver
    resolver: zodResolver(formSchema),
  })
 
  const onSubmit: SubmitHandler<FormSchemaType> = async (data) => {
    console.log(data)
  }
 
  // Form using React Hook Form
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      ...
    </form>
  )
}
export const Form = () => {
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<FormSchemaType>({
 
    // Making use of zodResolver
    resolver: zodResolver(formSchema),
  })
 
  const onSubmit: SubmitHandler<FormSchemaType> = async (data) => {
    console.log(data)
  }
 
  // Form using React Hook Form
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      ...
    </form>
  )
}