Now that we have our reusable card wrapper, let's go ahead and let's actually implement the fields which make the form. In order to do that, I want to go inside of my terminal here and I want to add a package from ShadCN. So let's go ahead and write mpx chat-cn-ui.latest-add-form. This is going to add a couple of elements but also a couple of packages inside of our app. And let's also, well let's take a look at everything it added, right?
So we can see that it modified our package.json. So when I click here, there we go, it added hook form resolvers, some Redux UI primitives for the label, it added React hook form and Zod for validation. So we have the form and the label but we are missing one specific field here. So let's go inside of the terminal here and let's add npx chat-cn-ui at latest add input like this. So this one does not add the input by itself so we have to manually add the input.
Great! So once you do that, make sure that you have npm run dev running, refresh your localhost to ensure everything is working. Now let's go ahead and let's go inside of our components, auth, and let's go inside of the login form right here. And the first thing I want to do actually is I want to create a form schema which I'm going to use inside of this form. And I'm going to keep all of my schemas in one place.
So let's go inside of the root of our application and create a new folder called schemas, like that. And inside, simply create an index.cs. So from here, we're going to do the validation on the front end, but also on the back end by easily importing it from this common folder here. So let's go ahead and import everything as Z from Zod, which we have installed. And let's also...
Well, no need to import anything else. Instead, we can export const login schema to be z.object, like that. And let's simply give it an email which is z.string and a type of email and let's give it a password which is simply going to be z.string. In the register form we're also going to add a minimum value of six but for login I don't recommend you do that because remember your password standards can change right So you might have some users which created their account before you updated the minimum length of your password. So it's kind of a good recommendation from me to you that you don't limit the minimum length of the password on login.
On register that's fine for new users right but on login make sure you don't block anyone if they have an old password which you allowed at the time. So just leave it like this. Great so now we have our login schema and let's go ahead and let's import inside of this login form everything we need to create our form. So we're gonna need use form from react hook form. We're going to need Zod resolver from hook form slash resolvers slash Zod like that and let's also go ahead and import everything we need from add slash components ui-form like that.
So we are going to need the form itself, the form control we're going to need the form field, form item, form label and form message like that and now let's go ahead and let's define our form here so I'm going to write const form to be used form like this and let's go ahead and give it a type in order to give it a type we have to import Zod so let's import everything as Z from Zod and let's also import our login schema from at slash schemas so just make sure that you exported this constant like that And then you can use the combination of those two. So this is going to be z.infer() type of LoginSchema. Like that. And now let's go ahead and give it a resolver to be ZodResolver which we imported and pass in login schema again. And let's now give it default values and because we added the types right here you can see how it will auto-complete the email to be empty and the password to be empty like this.
Great, so we have our form hook right here. And let's go ahead and let's see if I need to mark this as useClient I believe to get rid of these errors. Yes. So make sure you mark the login form component as use client. You can see how when we don't have that, we have some errors and they're not exactly clear what's going on, right?
But I assumed it's because the moment you use a hook inside of something you probably need to add well you definitely need to add use client at the top so just ensure you have that and your page should be well, working right and now we can use this form to actually create our elements So let's go inside of the card wrapper and let's add the form with the capital F right here so we imported that from this. Make sure you have all of this imported because we're gonna use all of those from components UI form. And what we have to do in this component is spread this entire constant. So let's go ahead and simply spread the form and that will give it all the props it needs except the children which we are going to write now. Inside this form write a native form element here and give it an onSubmit to be form.handleSubmit my apologies form.handleSubmit and you can just give it an empty arrow function inside like this.
So form which comes from this constant handle submit and then simply an empty arrow function we're going to change this with an actual submit function later and let's also go ahead and give this native form element a class name of space y6. Like that. So this is going to separate the inputs from the login button. And now we're going to create a div which is going to hold all the inputs and that one is gonna have spacing of 4. Like that.
Great! So let's go ahead and let's create our email field. So let's use the form field which we already have imported and form field is a self-closing tag like this. So make sure you have form field imported and make sure you use it like this. Let's give it a control of form.control Let's go ahead and give it a name and you can see how it only gives us these two options because it knows our schema.
And then let's use the render field to extract the individual field and immediately return a form item component like that inside of it a form label component which will say email and below it let's add a form control component and finally let's add the input which we forgot to import so let's go ahead right below here and Let's import the input from add slash components UI input like this. Great. And there we go. Now what we have to do is we have to give this input controls which match our form. So we can simply spread the entire field prop and that's it.
This is now a controlled component. Let's give it a placeholder of John Doe example.com and let's give it a type of email like this. And there we go We have our first field right here, which says email great And you can see how we have some validation. You can see when I press enter here this one has an error right here but we are missing the actual message. So you can try and press enter inside of the field when it's empty and you should get a little error here.
So what you can do is go outside of form control and add form message. I believe we have this imported as well. Form message. There we go. And you can see how now I have an error.
So if you go inside and press enter, you're going to get invalid email here. And if you ever wanna change those errors, you can. So for example, if you wanna change the one for the invalid email, I believe you can simply go inside of here, get a message and write email is required, for example. Let's see if that one is that and there we go. I just changed it to email is required.
So if you want to you can always modify, I believe for every additional chain you have the message object but if you want to modify the first one then it doesn't have the message as you can see but it does have invalid type error. So in here you can say must be a string or something like that. If you want to explore Zod more you can play around with it. I'm mostly going to leave the error messages as they are. Great!
So now what we have to do is we have to copy and paste this field and give it to our password, right? So go ahead and copy the entire form field like this. So here's where it starts and find the end of the self-closing tag. And inside of this div with space y4 spacing simply below with copy and paste it and you should now have two email fields. Let's change the bottom one to have the name of password.
Let's give it a label of password and let's go ahead and change the placeholder to be 1, 2, 3, 4, 5, 6 in the stars number, right? And type is going to be password like that and there we go, we now have our email and password here What I want to do next is I want to add a submit button. So let's go ahead outside of this div right here but still inside of the native form and let's add a button component. So we need to import that as well. I just did it automatically from ./.uiButton or .components.uiButton, however you prefer.
All right, make sure you added this button here. And I want to go ahead and write login. Like this. And I'm going to give it a type of submit and the class name with pool, like this. And there we go.
Now this is our submit form and you can see how it has that something is an invalid email until we give it a proper field right here. And yeah, let's go ahead and just add... I told you that you don't add anything here but just add a minimum value of 1 at least, right? And you can modify this message to say, password is required. So we are not explicitly gonna instruct the user that it needs to be six characters because this is not a registration form, right?
They already have a password. We cannot instruct them what their password must be because they could have created this account a long time ago when we allowed three characters or something like that so I believe now there we go it says password is required because if you don't add this message then it's gonna tell you that a minimum, you can see the string must contain at least one character. So it's kind of a weird error like this. So you can give this a password is required. And I'm also gonna modify the email then, message to be email is required.
Like that. Great, so now if I try and submit an empty form, there we go, email is required and password is required, looks much nicer. Great, so how about actually submitting the form? How do we go about doing that? Well, we have to create this empty arrow function which we started doing right here.
So go ahead and simply write const onSubmit or handle, sorry, onSubmit. Let's do it like that. And in here we'll have access to our values. And the values are going to be a type of z.infer open pointy brackets type of login schema. And if it comes along the values and add this on submit function here instead of this empty arrow function here.
So form.handleSubmit and then inside we'll pass in our onSubmit. So this wrapper will pass in the validated values right here so let's try it out so in here I'm going to open my inspect element here I'm going to give it a name of example, mail.com and 123456 for the password. And there we go. We have an email and we have a password right here. Great!
So there's a couple of more components that I want to create before we go into actually submitting this and that's the form error and form success components. So let's go inside of our components overall. So this time not inside of the out folder because this isn't exactly tied to out. So just inside of components add a form error.csx like this and let's go ahead and let's import rom at radix-ui-react-icons. So in the beginning of the project, I told you when we set up Shazzy and UI to choose the New York style.
If you chose the New York style, then this is the package you're gonna have by default for icons. Otherwise, if you chose the default style, you can try Lucid icons or Lucid React. I'm not sure what it is. But if you watched my previous videos, you're probably familiar with it. Basically, this is just to get the icon.
We also have FA icons so you can choose any icon you want. From here I'm going to export exclamation triangle icon meaning that something is going wrong. Exclamation triangle icon like this. I will create an interface form error props to optionally accept a message and let's export const form error like this Let's destructure the props form error props like this. Let's get the message out and if there is no message simply return null Otherwise, we're gonna return a div with a class name of BG destructive slash 15 padding 3 rounded medium flex oops where was I in the form error flex items center gap x2 text small and text destructive flex-items-center, gap-x2, text-small and text-destructive.
And then inside I'm going to render the exclamation triangle icon with a class name of height 4 and width 4. And I'm simply going to render a message inside of a paragraph like this. Great! So make sure you have this little component and now you can go back inside of the login form and this is where I'm gonna keep them. So I'm gonna keep them above the button here.
So if I add form error from dot dot slash form error, you can see how it automatically imported it here or components form error, if that's what you prefer, like I do. You can see by default, nothing is shown. But if I pass in a message to be something went wrong, this is where that's gonna pop up, right? Or for example, email taken, something like that, or invalid credentials. So this is where we're gonna keep those errors.
And I prefer it this way rather than a toast notification. Especially when it comes to, you know, notifying our user that we've sent them a confirmation email or something like that. I want that to stay here. We can use toast notifications later for different stuff. But for this one, I want it to be like this.
And besides form error I also want to have form success component and luckily for us it's almost exactly the same. So just copy and paste this here and rename this to form success and we're just going to tweak the colors and the icon a little bit. So rename this instances to form success. And let's go ahead and change the icon to be check circled icon or you know if you're using react icons just import whatever you like from here or use an emoji, it really doesn't matter. And now let's just change the colors, not be BG destructive, but instead Emerald 500 slash 15 and text is going to be BG Emerald 500 as well.
So just don't misspell emerald. You can hover over a class name if you have the tailwind extension to confirm that it exists. Great! And now we should have the form success component. So I'm going to copy and paste this and call this form success from ./.formsuccess I'm just gonna change this import to use components because I prefer it this way and instead of invalid credentials we're gonna say something like email sent or something like that and there we go you can see how this is going to look like.
Most of the time only one of those will be available. So either the success message or the error message like this. And just to clear this up, let's just go ahead and simply remove this to be an empty string like this. Great. So I'm gonna wrap it up for now.
What we're gonna do next is we're gonna create our first server action and get a little bit familiar with how we are gonna send the data to the server. You of course don't have to use server actions for this tutorial, it's just something that I prefer. If you prefer API routes that's perfectly fine, this project does not depend on server actions at all so you don't have to worry about that. Great, great job!