DEV Community

Cover image for Form validation with useForm hook
m0nm
m0nm

Posted on

Form validation with useForm hook

Form validation is a crucial and a must for every website that deals with user's data, But the form validation process can sometimes be a little complicated and messy, that's why solutions such as yup or Formik exist. Today we're gonna take a look at one of the react's popular hooks: useForm and see how we can validate user's input and display error messages should it be rendered.

Why React Hook Form ?

  • Built with performance, UX and DX in mind
  • Embraces native HTML form validation
  • Out of the box integration with UI libraries
  • Small size and no dependencies
  • Support Yup, Zod, Superstruct, Joi, Vest, class-validator, io-ts, nope and custom

Let's Get Started

I already made a simple registration form to apply on, Go ahead and clone the repo: Click me

react form

git clone https://github.com/m0nm/react-useform-example

Install the deps
npm install // or yarn add

After the installation has been finished now we're ready to use React Hook Form. But first we need to consider what are we going to validate, Since this is a registration form there are a couple of points to be mindfull of:

  • The email should be a valid email
  • Password must be at least 8 characters long
  • 'Confirm Password' must match the password

After setting the requirements for a successful registration it's time to use the hook!

Don't forget to Install the package!
npm i react-hook-form // or yarn add react-hook-form

How does it work ?

The useForm hook gives us helpful methods to simplify the way we handle forms, You can look up for all the methods on the API reference but the ones we need are: register, handleSubmit, getValues and formstate

register:

this method allows you to register an input field and assert the validations conditions for the input

It looks like this:

const { register} = useForm();

<form>
<Input {...register(name, options)} />
</form>
Enter fullscreen mode Exit fullscreen mode
  • name

Where name is the name to reference the input for the submit data:

register("email") 
Enter fullscreen mode Exit fullscreen mode

would output: {email: valueOfTheReferencedInput}

  • options

the way you condition the validation is by passing options props:

register("email", {
    required: true, 
    onChange: (e) => console.log("you're typing email")
})
Enter fullscreen mode Exit fullscreen mode

handleSubmit

this function will submit the data only if the form validation is successful

const { register, handleSubmit } = useForm();

<form onSubmit={handleSubmit(() => console.log("form validation is successful"))}>
  ...
</form>
Enter fullscreen mode Exit fullscreen mode

getValues

This is a helper function that allow us to get the value of our inputs, so there is no need to useState them.

const { register, getValues } = useForm();

const emailInput = getValues("email")
console.log("emailInput") // hello world 

<form> 
<input value="hello world"{...register("email")} />
</form>
Enter fullscreen mode Exit fullscreen mode

formState

formState is an object that provide information about our form state, One particular property we're interested in is the errors object which contains the error messages

const {register, formState: {errors}} = useForm()

<form>
  <p>
    {errors.email?.message}
  </p>
  <input {...register("email")}/>
</form>
Enter fullscreen mode Exit fullscreen mode

Let's Apply

First we need to register our inputs with the register method, so go ahead and do that:

Note that we don't need to have name attribute on our inputs since it handled by register method.

import {useForm} from "react-hook-form"

function App() {
 const {register} = useForm();

 return (
    <form>

      {/* email */}
      <input {...register("email")} type="email" id="email" />

      {/* password */}
      <input {...register("password")} type="password" id="password" />

      {/* confirm password */}
      <input {...register("confirmPassword")} type="password" id="confirmPassword" />   

        </form>
    )
}
Enter fullscreen mode Exit fullscreen mode

Now we need to give each one of them the validation's conditions

  • For Email: It is required and It must be a valid email, so we can use required and pattern

The pattern value is called a regex expression


 {/* email */}
 <input {...register("email"), {
        required: "Please Enter Your Email!",
        pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, 
            message: "Please Enter A Valid Email!"
        }
    }} type="email" id="email" />
Enter fullscreen mode Exit fullscreen mode

If the user did not type his email, the errors object from the formState will contain the message we typed with the required property, And so it's the case if the email is not valid it will contain the message from pattern

Now we can show the error message

<p className="error-message">{errors.email?.message}</p>
<input ... />
Enter fullscreen mode Exit fullscreen mode
  • For Password: It is required and it must be longer than 8 characters
<input {...register("password"), {
        required: "Please Enter Your Password",
        minLength: {
        value: 8,
        message: "Password must be at least 8 characters long!"
        }
    }} type="password" id="password" />
Enter fullscreen mode Exit fullscreen mode

The error message

<p className="error-message">{errors.password?.message}</p>
<input ... />
Enter fullscreen mode Exit fullscreen mode

-For Confirm Password:
This one is special because it only need to match password for validation to pass, so we can use validate option's method, We also need getValue to get the password value

<input {...register("confirmPassword", {
        validate: (match) => {
            const password = getValues("password")
            return match === password || "Passwords should match!"
        }
    })} type="password" id="confirmPassword" />
Enter fullscreen mode Exit fullscreen mode

And the error message

<p className="error-message">{errors.confirmPassword?.message}</p>
<input ... />
Enter fullscreen mode Exit fullscreen mode

Now we're finished with the validation! It's time to use handleSubmit

<form onSubmit={handleSubmit(() => console.log("user registered successfully!"))}>
    ...
</form>
Enter fullscreen mode Exit fullscreen mode

Here is our form if we submit without filling the data

react form errors

And if we don't meet the conditions

react form errors

You can find the final code on this repo: Click me

Integrating with a schema validator

As an alternative for register options, You can use a schema validator instead such as yup which i'll use to demonstrate.

  • Install the dependency npm i yup // or yarn add yup
  • We also need the yup resolver for react-hook-form npm i @hookform/resolvers/yup

Let's define our schema

import * as yup from "yup";

const schema = yup.object().shape({
  email: yup
    .string()
    .email("Please enter a valid email!")
    .required("Email is required!"),
  password: yup
    .string()
    .min(6, "Password must be longer than 6 characters!")
    .required("Password is required!"),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref("password"), null], "Passwords must match!"),
});


function App() {
    ...
}
Enter fullscreen mode Exit fullscreen mode

Lets use the schema

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";

const schema = yup...

function App() {
     const {
    register,
    getValues,
    formState: { errors },
    handleSubmit,
  } = useForm({ resolver: yupResolver(schema) });

    return (
        <form>
            <p className="error-message">{errors.email?.message}</p>
            <input {...register("email")} />
                ...
        </form> 
    )
}
Enter fullscreen mode Exit fullscreen mode

And that it!

Conclusion

We reached the end of this post, Thank you for reading i hope it was helpful and clear :). Happy react-hook-forming!!

Top comments (0)