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
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>
- name
Where name
is the name to reference the input for the submit data:
register("email")
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")
})
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>
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>
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>
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>
)
}
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
andpattern
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" />
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 ... />
- 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" />
The error message
<p className="error-message">{errors.password?.message}</p>
<input ... />
-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" />
And the error message
<p className="error-message">{errors.confirmPassword?.message}</p>
<input ... />
Now we're finished with the validation! It's time to use handleSubmit
<form onSubmit={handleSubmit(() => console.log("user registered successfully!"))}>
...
</form>
Here is our form if we submit without filling the data
And if we don't meet the conditions
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() {
...
}
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>
)
}
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)