Build a React Form with react-hook-form and zod
Learn how to build a form in React with react-hook-form and zod.
Published June 11, 2024 (Updated June 11, 2024)
In this tutorial you will learn how to use react-hook-form and zod to build a form with validation.
If you prefer a video tutorial instead, you can watch it below.
Clone the project from GitHub.
This is what we are going to be building:
Let's start with a little bit of boilerplate code for our form component:
This just gets us the form with styling without any functionality added yet.
Building a form validation schema with zod
Let's build a schema that matches the values in our form.
Let's start off by importing the necessary libraries:
And let's define our schema with some custom error messages:
We will use a string validation chained with an email validation for our email field.
For the accept terms of service checkbox we will use literal validator with the value of true
. Literal just means that the field must be exactly this value. Notice that we are also using a custom error message for invalid_type_error
. Later in this tutorial you will learn how to show the error messages.
For our payment tier validation we first check if the value is a string and then use a custom validation using refine
to check if the string matches one of the IDs from our predefined Tiers
array.
Let's infer a type from it that we are going to use moving forward:
We can see that TypeScript inferred the following type from it:
This will help us keep all our functions type safe.
Using react-hook-form
Let's use react-hook-form to handle our form state.
Add this code inside your Form
component:
We have used the useForm
function and given it the type of our schema. This will help TypeScript to properly keep our code type safe.
We have created a onSubmit
function that after a 3 second delay will log the validated form data into the console. I wanted to add an artificial delay to better emulate a real world scenario.
If we try filling the form and submitting it, nothing happens. This is because we haven't yet registered the form inputs or made the form to use our custom onSubmit
function.
Registering inputs
We can register the form inputs by using the register
function we get from useForm
by giving the name of the field that matches the one in our schema.
For example for the email field:
And accept terms of service:
And for the payment tier radio button:
Using custom onSubmit handler
The handleSubmit
function we get from useForm
does two things. First it disables any default form submission behaviors, and second it calls our custom onSubmit
function with the validated data.
Now if you try filling the form and submitting it you will see that after 3 seconds the validated form values get logged into the console.
If you fill the form with invalid values you will see that the correct error messages appear.
One problem you might have noticed is that you can click on the create account button multiple times and the form will submit multiple times. This is obviously something we don't want to happen.
Let's fix that by disabling all form inputs and the submit button when the form is submitting.
Disabling form inputs
We will use the isSubmitting
value we get from formState
which we get from useForm
to check whether the form is currently submitting or not.
For our inputs and submit button we will disable them using this value.
Example for our email input:
Add the disabled={isSubmitting}
attribute to other fields and the submit button.
Now when you submit the form you will notice that all the fields and the submit button get disabled until the data gets logged into the console.
But what about if the form is not valid?
Show error messages
Currently if you try submitting a form with invalid fields, nothing happens.
Let's change that by conditionally showing error messages for each of the fields if they are invalid.
For our email field:
and accept terms of service button:
and for payment tiers after the ul
tags:
Now when you try submitting the form with invalid fields you should see the error messages showing.
The default behavior of react-hook-form is to validate the form when submitting for the first time. After this it will validate the form after every key press and blur event.
If you have enjoyed this tutorial so far you will surely love my YouTube channel as well. I have multiple high-quality tutorials there.
Conclusion
In this tutorial you learned how to combine react-hook-form and zod to create a fully fledged form with validation.
For next steps, dive into the react-hook-form documentation to learn more advanced concepts such as: dynamically generated fields and multi step forms.