React gets a lot of things right. The solutions that it provides and the paradigm shifts that it offers us when building web applications work really well. But, unfortunately, nothing is perfect.
There are some things in React that if you try to do them natively, without any sort of third-party library, you're not going to have such a good experience. One of such things is forms. If you try to build a form natively in React, oftentimes you end up spending more time trying to prevent the default behavior rather than encouraging it.
So, in this video, I'm going to show you an alternative to building native forms in React. It's called React Hook Form and it's a form library that makes it really easy for you to build complex and performant forms in React. Let's get into it.
All right, let's begin. So, first, I want to talk about and show you why you would want to use a form library like React Hook Form in the first place. So, here what I have is I have a component that renders out a very simple form.
It just has an email and a password field. This is the JSX that is rendering this form. You have the HTML form right here, you have the HandleSubmit function, and then you have your two inputs, one for the email and one for the password.
The code that's running this form is actually quite simple. You have two states here, one for the email, one for the password. These are getting sent to the actual inputs to update those values.
Then you have a state here for the errors. This is a simple object with just the email and the password as properties. And then you have the HandleSubmit function, which will take the actual form event, will first prevent the default, then will set the errors to null because this is a new form submission, and then will perform some very basic, simple manual validation, like just checking that the email has an at symbol.
And then if we get here to this part of the code, we consider the form validated, and then we console log form submitted. This is what the form looks like. You have your email and password field.
If I press submit, we get here this error that basically shows that the email must include an at symbol. And then if I just add the at symbol and I put something here, and I submit it, we actually get a log in our console that says the form has been submitted. This form works.
Now, if you look at this code, just like this, you might ask yourself, why would we even need a form library? I mean, this works just fine. And you're right, it does.
But the thing is, this is a very simple form. This only has two properties, the email and the password. And for simple forms like this, I actually wouldn't even recommend that you use something like React hook form, because it's overkill.
For a simple form like this, doing it this way is completely fine. The problem, however, is the fact that this form doesn't scale well at all. This is a very simple form.
And for simple forms, this works. But as soon as you want to add any sort of complexity to it, even just adding more fields here, this starts to get very complicated very quickly. For every new field that you want to add, you have to basically add a new state property.
You have to manage that field. You have to manage its updates. You have to then pass it to the actual input.
So you would have to add another input as well, add another onChange property here and the value here. And if you have a form with like 10 inputs, you can quickly see how much code you're going to have to write just to get that working. Also, any new field that you add, you have to also remember to put it here in this arrows object, which also means that you have to put it here to make sure that it gets reset properly.
And then you have to handle your own validation. Even worse, let's say that instead of console logging that the form is submitted, you actually want to take its data, send it to a server and await the service response, you would want to turn this into an asynchronous function. Well, if you did that, you would also probably want to show something to the user as this form is being submitted to display some UI that something is happening.
You might also want to prevent this button from being clicked while a form is submitting to prevent a form from being submitted multiple times. To do that, you would have to come here and create your own loading state. You would then have to make sure that you're properly handling the loading state inside of this function here.
So that means setting it to true before the form submits and then setting it to false after the form has submitted and got a response back from the back end. And you can quickly see that this is becoming very complicated and you have to write a lot of code just to get a form to work. And that is why you would want to use a form library like React hook form.
React hook form allows you to do all of this and more with a lot less code and a much better development experience. So now let's actually get rid of all of this code and let's reimplement the same form using React hook form. So first I'm going to delete all of this code because I'm not going to need it.
Then in the form here, I want to get rid of this on submit function. I want to also get rid of the value and the on change on the inputs. And I'm also going to get rid of these errors here.
So let me do the same for password, get rid of this, and then we can get rid of this imports here because we no longer need them. Now we just have a simple basic form without any functionality. To create a form with React hook form, we're going to make use of the use form custom hook.
So we'll come here at the top and we'll import use form from React hook form like so. And then inside of the component, we're going to come here and we're going to write const form equals use form like so. This is the most basic version of use form.
Now if you're using TypeScript, there's one more thing that we have to do. If you're not using TypeScript, it's totally fine. You can just skip this very short part.
We're going to define here the types of our actual form fields. So we're going to do type form fields, and that is going to be equal to email string and let copilot complete the rest. We're also going to have password string.
So these are going to be the actual form fields that we're going to use within our form. And then we're going to take this form fields and we're going to give it as the type of use form like this. We're going to do form fields.
And so this will make it so that whenever we work with anything form related in React hook form, we're always going to have this properly typed. The next step for us to do is to actually connect this form element here and these inputs here to React hook form. Because just by writing this code, it's not enough.
We actually have to connect all of these inputs and HTML elements for them to be able to work with the form. And the way that we do that is we're going to make use of the register function that we get by accessing use form. So I can actually destructure this form variable here and I can do register this function.
I can use it on an actual input and then register that input with React hook form. So I'm going to come here and start by registering the email input. So I'll come here.
I'll do curly bracket dot dot dot register and then call this and then give it here the actual field. And because we have properly typed these fields, we get here auto completion, email or password. I'm going to select password.
And now this input field has been registered to the form on this email field right here. Let's now do the same thing to the password input. So we'll come here, we'll do dot dot dot register.
And this one will give password. With this, both of our inputs fields are registered to the form. So this means that now whenever they change values, whenever we type in these input fields, that value is going to get sent to React hook form, and it is going to manage everything for us instead of us having to do it ourselves.
Next, we actually need to also connect this form element here so that on submit, we actually call our own custom on submit function instead of submitting it to the default behavior, which is to send it to an actual URL. So first, what we're going to do is we're going to define our own custom on submit function. So we're going to do const on submit.
And now if you are working in TypeScript, you're going to want to type this as submit handler, which you can import to directly from React hook form, and then give it form fields. If you're in JavaScript, just don't do this, it's totally fine, then it's going to be equals data. And then we're just going to for now, console dot log data, and then close this out right here, typing this function as submit handler, and then form fields is actually going to make this data here be properly typed.
And as you can see, this has the type form fields, which is basically this object right here. So the data that we're receiving from this function is not actually the form event that we had previously in the first example. But it's actually just an object with the email and the password and their corresponding values.
And the reason for that, the reason why we're not getting the form event directly is because we're not actually going to be passing this on submit function to the form, we're actually going to make use of the handle submit function from React hook form and pass that instead and give this function as an argument to this handle submit. So I'm going to come here to the form. And then I'm going to do on submit, I'm going to I'm going to give it handle submit, and then on submit, like so what this is going to do is that whenever this form gets submitted, it's going to first call the handle submit function, which remember comes from React hook form.
And this will do some work behind the scenes for us, for example, it'll prevent the default behavior of the form. And then it's also going to make sure that our form fields here are valid before then calling this on submit function with the actual form fields. So it doesn't give us the form event, it actually just does all of the work of validating the inputs for us.
And then once they are valid, it calls this function with the actual form fields, which is great, because that means that we don't have to do all of that work ourselves. And now with this, we actually have a complete form using React hook form. So now let me go back to the actual application.
And let's see our form right here. Let me just reload. So we get a clean slate.
And now if I press submit, we actually get a lock here in our console with the email and the password as empty strings, which is great. Our form now works exactly asAs we expect, but we still have more work to do. Right now, we can actually submit this form without putting anything in either of these input fields.
Let's add some validation to these input fields and make sure that we have at least some basic validation before we submit our form. Now, there are multiple ways that you can validate inputs in React Hook Form. I'm going to first start by showing you the simplest one.
The simplest way to validate an input is actually to use this register function right here. This function takes in an optional second parameter, which can be an object for that specific field. And in this object, you can give it some properties to define how this field gets validated.
For example, we can give it here, required, true. This is going to make it so that this field is required to at least have some value in it before it is considered valid. We can then come here and add the same thing to the password field, so we'll give this here an object, we'll do required, true.
Now, if I go back to our application and I refresh, we can no longer submit the form. I'm pressing submit, but nothing is being logged until I actually write one letter and one letter in this field. Now, it's going to pass the validation and we actually get our form submission.
Besides required, you can also do a regex pattern. So you can do that by doing pattern like this, and you can do a pattern here. Let's let Copilot write one for us and see how that does.
Let's come back here and let's put anything here, one letter, one letter. It's not going to let me submit it, but as soon as I type a valid email, it should theoretically, yes, let me submit it. You can do that with a pattern too.
For the password field, we might want to add a minimum length, which we can do with minLength. We can give this eight, for example, and now this is going to be required to at least have eight characters. So now if I go back to the form and I type our email.
com, I'm going to do one, two, three, four, five, six, seven, eight. And now I'm going to do, I actually think I type nine characters, but you get the idea. If I put anything less than eight, it's not going to submit.
We now have a more robust validation solution in React hook form. Also, if you want to do your own custom validation, you can also do that. So let me remove here this pattern here and let's do validate.
You can pass it a function that will receive a value, and then you can actually write some code with that value. For example, we can make a check that the value includes at least an at symbol. So now if I go back to the form, I can do this and I can type any sort of invalid email.
And as long as I put an at symbol here and I put one, two, three, four, five, six, seven, eight in the password, it will submit the form. If I remove the at symbol, it's no longer going to submit the form. There are actually more things that you can put in here.
Things like max length, min and max. I'm not going to go into them on this tutorial, but I would recommend that you go and look at the documentation for React hook form to see all of the things that you can put in here. Form is better, right?
We can actually submit. We have proper validation, but we're not actually showing anything to the user. If the form is invalid, I'm pressing this button here and nothing is really happening.
Ideally, like we had it before, I want to display an error if a specific input field is invalid. And the way that we're going to do that in React hook form is we're actually going to access the form state from this use form hook. So besides register and handle submit, you also get access to form state.
And this one is going to be an object that you can destructure and you can get access to the errors object in here. This will actually have, if I hover over this, it's going to be field errors and then form fields. This will be an object with each of our form fields and a corresponding error if there is.
And then we can actually use this to come back here to our JSX and right below our email input. For example, we can come in here, open a curly bracket and do errors. email and end, and we're going to do div and we're going to put here errors.
email. message. We'll get back to message in just a moment.
Let me just add here some text so that this error becomes red. So we'll do class name, text red 500. Let's save this and do the same thing for the password.
So come here, do errors. password. And now we actually are going to show an error if there's an error in that specific form.
So now if I go back to the form and I press submit, we actually have something that happens. Let me just close this so you can see, but we're not actually getting any errors. We do have more space between these fields here, but we're not actually seeing our red errors.
What's going on? Well, if you actually look at our code, we do have this div that is being rendered. So this errors.
email must be true because otherwise this div wouldn't rendered the same for the password. But this message here is not rendered. If I actually hover over this message, you're going to see that this is an optional property.
This can be either string or undefined. The thing is, we have set our errors correctly in the sense that React hook form is handling our errors with our validation, but we haven't actually defined any message for a specific error. So this is actually undefined.
So to fix that, we actually have to implement error messages when our validation is incorrect. And the way that you do that with React hook form is actually super simple. Instead of returning a Boolean here with this specific validation, you can actually return a string instead that will get used as the error message in case that validation fails.
So let's do it. Let's remove true. And it's your return email is required.
Let's now do the same thing for password, remove the Boolean and do password is required. So now if I go back to the form and I press submit, we actually have the error messages that are being properly displayed. This is what we want.
Cool. So let's go back to our code and let's actually complete these for the validate function and also for the minimum length. So here in this validate function, instead of returning a Boolean, we're going to take this code and we're going to put it here as a function and we're going to do if value includes and let copilot complete everything for us, we're going to return this string right here.
And also here we have to return true because in the case where the input is valid, you still have to return a Boolean. It's only when it's not valid that you can return a string and that string will get used as the error message. In the case of our password where we have this min length here, we still have to return a number somehow, but we can actually do it by passing an object here.
Instead, we can do value eight and then message password must have at least eight characters. Now if I save all of this and go back to our form, where's our form? There we go.
And I can reload. I can press submit. It's going to say email is required.
As soon as I type one letter, this error now changes dynamically and automatically to email must include at. If I then put at this input is going to be considered valid and now we no longer have an error. The same for the password.
I can type one letter and now it's going to say password must have at least eight characters. If I can type seven more, one, two, three, four, five, six, seven, then we have no more errors and our input is valid. I can submit this and I can open up the console and I can see our values right here.
Now we have proper validation in our form. This is fantastic. Now let's take it one step further and let me show you something else that react hook form makes really, really easy to do.
Let's say that now we want to convert this on submit function into an asynchronous function. Instead of just console logging the data directly, we actually want to await something, maybe sending it to a server and then only once the response is returned, do we then want to continue. And also we would want to show that to the user.
We will want to show to the user that the form is indeed being submitted. And on top of that, we should also disable this button while the form is being submitted. So let's do that.
Let's first convert this function into an asynchronous function. There we go. And now I'm going to do a wait and let's see if copilot can do it.
Yes, it can. I'm just going to await something for one second, just so that you can actually see what's going on. React hook form makes handling asynchronous functions really, really simple.
There's actually another property that you can access in this form state besides errors that is going to be is submitting. This property is a Boolean that will be true when the form is submitting. And that's all that you have to do.
You don't have to manage your own loading state. You don't have to handle anything. React hook form is doing all of that for you.
So then we can actually just use this property right here. We can come back to our button and we can do disabled is submitting. And actually, let's change this text here to actually make it show something while the form is submitting.
So I'm just going to remove this. I'm going to do is submitting, question mark, loading, otherwise submit. If I now save this and I go back to the form and I refresh just so we have a clean slate, I'm going to type here an at symbol because that's where our validation requires.
And then here, 1, 2, 3, 4, 5, 6, 7, 8, I press submit. It says loading. And then one second after we actually get the lock in our console.
Once again, I press submit loading one second after we get the lock in our console. And just like that, our form now supports asynchronous functions. And all that we had to do was literally just convert this to an asynchronous function and just await something inside of it.
And React hook form takes care of the rest. Now let me show you another really cool feature about React hook form. Let's say that instead of just awaiting here an empty promise, we were actually sending something to a backend.
The backend can sometimes fail. It can sometimes throw an error if the form is invalid. If this was a sign in form, maybe the user's credentials weren't valid.
And so the backend would actually return to us an error. And we need to handle that error. We need to show that error to the user.
And ideally, we need to plug it inside of our form. Well, to do that, React hook form makes this super easy for us. Once again, we can actually access another function here called set error, and this willAnd the last thing we can do is we can pass it here an object with a message that can be, this email is already taken.
And then the last thing that we need to do is we need to throw an error in this try block here, so that this catch gets executed. So I'll do, throw new error. And I'm just going to do it like so.
Now, if I go back to the form and I refresh, so we have a clean slate, I'm going to first make sure that this form is valid. So I'll put this, 1, 2, 3, 4, 5, 6, 7, 8. When I press submit, it's going to load for one second, and then it's going to throw this error here on the actual input field.
This email is already taken. We've now just gotten an error from the backend, an asynchronous error, and we've plugged it inside of our form directly using setError. Now, what if we wanted this error to actually not belong to any specific input field, but rather to belong to the form as a whole?
Well, we can actually do that by simply replacing this email here with root. If you saw before, let me show you again. We have three options here.
We have email, password, and root. We can use root for an error that does not belong to any specific input field, but rather belongs to the form as a whole. And then all that we will need to do is come back here to our actual JSX, and then just render out this error.
So what I'm going to do, I'm just going to take this entire block, and I'm going to put it here right below the button. I'm going to do errors instead of password. root.
And that's it. With just this, if I now go back to the actual application, and I close this console so you can see, I will press submit again. This error is going to get disappeared, and now this error appears here at the bottom.
This is how you can easily have an error that comes from the backend, an asynchronous error that doesn't necessarily belong to any single input, and you can display it in your form. Now, there's one more thing that I want to show you about React Hook form that you're probably going to want to be using. Let's say that instead of having here a fresh form, a form with nothing in it, we actually want some default values in our form, because oftentimes we're not creating an entity, but we're editing an entity, and we want the properties of that entity to be pre-populated in our form.
And the way that you implement that in React Hook form is actually really simple. All that you have to do is come here where you have use form, and in this parenthesis here, just give it an object, and you can actually specify default values, and then you can give it here any default values that you want. For example, we can do email, and we can do test email.
com. And note, you don't have to provide every single field in here. You can just provide the ones where you want to have a default value.
If I now go back to the form, and I refresh, our email field automatically has test at email pre-populated. So you can use this to super simply add default values to your form. And now as a bonus, let me actually show you the recommended way to use React Hook form.
There's actually a better way to handle our validation here that is actually a lot less code, and that is going to involve using Zod as our validation library. And for this, you're actually going to need to install two more things besides React Hook form, because they come separate. The first one is the hook form slash resolvers library, which will give you access to some resolvers that you can plug into your form.
And the second one is going to be the Zod library. This is a very popular validation library. It does a lot more than just validation, but it works really nicely with React Hook form through this resolvers library.
Then once you have those installed, you can come back here to the form, and you can start by writing import Z from Zod. And then we're going to use the Z to actually define a schema for our form. And I'm going to let Copilot be very helpful and define it for us.
So we have here const schema equals Z. object. And then this object is going to have two properties, email, which is going to be Z.
string. email. This is very important.
And then password, Z. string. min8.
This video isn't going to be a tutorial on Zod. I do plan on making one. I just haven't already.
So if you're watching this at a later point, it might already be out. But for this purposes, this is all that we're going to do with Zod. Then the next thing that we want to do is we can actually get rid of this form field because we can infer it from the schema directly.
So what I can do is I can just do here type form fields equals Z. infer type of schema. And actually, let's just get rid of this so we don't have duplicates.
Basically, we have the same type here, email, string, password, string. But this is being inferred directly from the schema here. This is also one of the benefits that you get from using Zod.
And then in our form, we can come back here to where we have used use form. And besides default values, we can do here resolver. And we can pass it here, Zod resolver.
We will import this from at hook form slash resolvers, the library that we just installed. And we're going to pass it here, the schema. What this is going to do is this is going to connect our schema to React hook form using the Zod resolver.
And this schema from Zod actually has a lot of validation already built in. If you just look at email here, email is going to first make sure that it's a string, which it should be. We have an input of type text.
But then it's going to make sure that this string is a valid email. This dot email here is going to perform all of the validation necessary to make sure that this is a valid email, which if you remember, is a lot more than what we did for our form. Our only validation was inside of this function here.
And we only checked that the value has an at symbol. This dot email will actually do a lot more checks and will only return true if this email is valid. So this is great because we did all of our work and more with just one simple line of code.
And then for the password, we're still checking that it's a string. And then all that we have is dot min eight. We're basically checking that this is at least eight characters, which is exactly what we had defined here previously.
So now because of this, we can actually remove all of our own custom validation. So we can come here and we can remove all of this directly. And we can just use the register like this, like so.
We can do the same thing with the password. Come here, remove everything. And now our code looks a lot simpler because all of the validation is defined in Zod.
And we don't have to do it ourselves. If I now open up our form again and I press submit, we actually get the same error, although it has a different error message because now this is coming from Zod. But it's saying that string must contain at least eight characters.
If I then put something in here, it's not going to get removed until we have eight characters. If I remove the email here, it's going to say invalid email. I can type a letter.
It's still invalid email. I can put an at symbol. It's still invalid email.
But as soon as I do email. com, then the error goes away. And this is now a valid email.
This is the recommended way to use React hook form and actually to build forms in React. Using React hook form, you get a lot of this functionality pre-built already. You get all of the state management, error handling, and more.
And using Zod as your validation library, you then outsource the validation to Zod so that you don't have to do it yourself. And also, one of the little benefits of using Zod is you no longer have to even define your own types. If you're using TypeScript, they can get automatically inferred from Zod.
So if you ever find yourself building a form in React, consider using React hook form and consider using Zod as your validation library. If you enjoyed this video and you want to see more tutorials just like these, make sure to leave this video a big thumbs up. You can also click here to subscribe.
Or you can click here to watch a different video of mine that YouTube seems to think that you're really going to enjoy. With that being said, my name has been Darius Cosden. This is Cosden Solutions.
Thank you so much for watching. And I will see you in another video. Ciao, ciao.