Forms 0 exercises
lesson

Next.js App Setup

To start, run the create-next-app package using the npx command. We'll use the "--use-pnpm" option to ensure all dependencies get installed using the pnpm package manager.


npx create-next-app@latest --use-pnpm

When the prompt appears, we'll name the project app-router-forms. S

Loading lesson

Transcript

00:00 All right, so here's how I do it. I'm going to use the mpx command to run the create-next-app package at the latest version to get the latest code. And then I'm going to use this use-pmpm option so that it uses pmpm as the package manager as opposed to npm. I'm a pmpm fan, and this will just make sure

00:18 that all the dependencies are installed using pmpm. So I'm going to name my project AppRouterForms. You can name it whatever you want. I'm definitely going to use TypeScript. You can use ESLint if you want or not. I'm definitely going to choose Tailwind because ShadCN relies on Tailwind. I'm going to choose to use the source directory.

00:37 That puts all of the source for the entire application in the source directory within the top level. So that keeps the top level of the app really clean. I'm definitely going to choose to use the AppRouter. It's recommended. And I'm not going to customize my import alias. This just means that @, if I use it in import,

00:54 will be relative to the source directory. All right, I'm going to bring that up in VS Code. The next thing we need to do is initialize ShadCN. So I'm going to go to my console. And then I'm going to initialize ShadCN using the mpx command again, this time with the ShadCN UI package.

01:12 And the init command, that's going to initialize the project so that it's ready to get any components we want to bring in from ShadCN. We get some options here, like the style that we want. I'm just going to pick the defaults. And then I'm going to say that I want to use CSS variables for colors. And we're initialized.

01:32 Now let's actually go take a look at what's happened. So it's gone and created a components.json file in our root directory. That's basically for this mpx ShadCN command to know where everything is, what my style preferences are, and all that. It's gone and made some changes to the tailwind configuration file to add CSS variables.

01:50 And then it's defined those CSS variables over in our globals.css, with the actual colors. Back at the top level, let's look at package.json. Now we can see it's already added some libraries, just to support the basic shell of ShadCN. So now let's go and add in our first components.

02:10 Now the most critical one we want to add is the form system. So we're going to use mpx again, and then ShadCN UI. In this case, we're going to use add instead of init. We're already inited. And we're just going to give it the name of the component system we want, which in this case is form. Now I say component system, because form actually brings in a bunch

02:27 of components out of the box. You can see your ShadCN components over in the components UI directory. In this case, it's brought in button, form, and label. Now the interesting thing about ShadCN is that you can actually edit these files. They're there to be edited and customized in your project.

02:43 It's not like other UI libraries, where you can't actually edit the components. Here, they actually just copy it in your project, and you can make the changes. I'm not going to make any changes, but you can if you want to. Now it's interesting. Having added form, ShadCN automatically added some new dependencies

03:01 for us, including React Quick Form to manage our forms, resolvers, which allow you to connect schema validation libraries like Zod or Joy or AGV to React Quick Form. In this case, it also brought in Zod, so we're going to use Zod to define our schema.

03:20 Now the format we're going to build has only input controls, so you also need to bring in the input component. To do that, we use the exact same ShadCN command. We'll just bring in input. And there we go. We've got our input field. Now that our application is configured, let's bring it up in development mode.

03:38 To do that, I use pnpm and then dev. It's got a stark white background. I want to work in dark mode, so the first thing I'm going to do is set the body to dark. To do that, I go to the app and then global CSS, and then add body, and then apply the tailwind for dark.

03:57 That applies the dark mode theme. Let's go take a look in browser. And now everything's dark. Of course, I've got all this boilerplate in there, so let's get rid of all that. To do that, we just go into page.tsx. And then we remove everything and replace it with a div.

04:14 We go back to our page, nice and blank and ready to go, ready for our form. So we're going to build a user registration form. It's going to have a first name, last name, and an email field. And the first thing we want to do is build a schema for that that we can validate any user input against.

04:31 So let's go and create a registration schema in a file called registration schema.tsx. Then we'll bring in Zod. Zod's what we're going to use to define our schema. Z is just an object. It's got a lot of little helper methods in it

04:47 that allow us to create a schema that we can then validate against. Now, our base object here is going to be an object. It's going to have keys for first, last, and email. So we use Z.object to define our object. And we export that as schema.

05:05 So let's define our first field. We'll say that first is going to be a type of string. So we use Z.string. Now, there's two types of things that you can do with Zod. You can have it clean up the data. In this case, we want to trim it down. So let's use .trim. Now, we'll trim any white space off the left and right-hand sides

05:24 of the string. And then we apply any validations we want. In this case, the validation we want to use is the minimum number of characters. We want to make sure that they've actually typed something in. So we say that we want at least one character. And if you fail that, then the message is that the first name is required.

05:42 So let's do the same thing for last. Exactly the same thing, except last instead of first. And we massage the message a little bit. And then finally, we want an email field. So we do pretty much the same thing. But instead of min, we use email for our validation. Now, we don't need to define any extra parameters on email.

06:02 In this case, we just need to say that the message is invalid email address if it turns out to be an invalid email address. Now that we've got that going, we actually want to create a registration form. That's going to be a client component. It's going to use a hook.

06:18 In this case, that is the use form hook from React hook form. But before we do that, let's just create a simple registration form. And it'll bring in use form. And then we'll invoke that within our registration form. So use form can take a lot of different parameters. The one that you often use is the default values.

06:38 So in this case, first, last, and email are going to be defaulted to an empty string. So now we want to connect that to our schema. Let's bring in our schema from the registration schema. Now, to connect those two, we need a resolver. So we're going to bring in the Zod resolver from Hook Form Resolvers. Hook Form Resolvers has resolvers

06:56 for all kinds of schema validation libraries. There are a bunch of them-- Joy, YUP, AJV. You can go and check out the full list on the Hook Form Resolvers NPM package. Now, down to use form, we set the resolver to the output of Zod resolver with our Zod schema.

07:15 Now, how do we know that first, last, and email are the right ones? We could just put in foo here. And that may or may not be right. So what we really want to do is tell use form what the structure of our schema is in TypeScript.

07:33 To do that, we use a template syntax after use form. And now we see that use form has applied that schema to the default values. And so foo is invalid. That's cool, but really, we don't want to have to type all this. We want this just to come from the schema. So is there a way to infer that from our Zod schema

07:52 and turn our Zod schema into TypeScript? Well, yes, there is. First thing we need to do is bring in Zod. And then we can create a new type called our schema, for example, by inferring using the z.infer utility type against the type of our schema.

08:09 And what that comes out with is that exact same thing. We've got first name, last name, and email, and everything's in TypeScript. Awesome. So we can use that here in place of what we had before. Or we just want to keep it nice and terse.

08:27 We can just put that in here instead of our schema. And again, foo is wrong, so let's get rid of that. Nice. Now our use form is configured to match the TypeScript schema of our registration form Zod schema. So let's start building out our form. So that brings in some UI components,

08:46 including button and input. We need the button in order to submit, and we need the input in order to take input from the user. And then we're going to bring in the form components from ShadCN. That includes form. That's a wrapper around the form. Form control, which is a wrapper around individual controls

09:06 within the form. And then a bunch of helper components like field, and item, and label, and so on and so forth. If you want to know more about this, there actually is excellent documentation on this. Over on the ShadCN site, let's go to form. So they got some excellent documentation on React Hook

09:23 form and how it integrates with ShadCN. This is only client side. We're going to cover both client side and server side. In this tutorial. But there is an example section down below that you should have a look at, where they cover different types of controls and how to use them. For example, checkbox, date picker, radio group, and so on. Let's go back to our code, and we'll

09:43 start building out our JSX for our component. So the first thing we want to do is bring in that form component, and then give it all of the output from our use form. That's going to give it all of the controls from use form, so that that component can then manage that form. But we do want a legit form tag inside of that.

10:01 Let's add a Submit button, so we can submit that form. And then let's add the email field. To do that, we're going to add the form field component. To add the email field, we add form field. We give it the control that we get from form.control. That allows this form field component

10:19 to manage this particular field. And then we give it the name of the field, which in this case is email. If I remove email, you can actually see that our pop-up hints to all of the available fields that we have. Don't bring back email. The last thing it needs is a render function. So render gets given field, and then you

10:39 get to specify how you want that actual field to be laid out and all the components you're going to use. In my case, I'm going to use a form item. And then inside the form item, I'm going to have a form label that has the label. Then the form control that has our input. And we give that form control, in this case the input, all of the output of field.

10:57 That's going to have the current value, on change, on blur, and all that good stuff. And then the description, which I'll just set to your email address. And then the message is going to have any validation messages. So I think this is good enough to actually see. Let's go back over to our page, and then bring this in,

11:15 and then use it. Let's see. All right, looking pretty good. Although it does take up the entire width of the screen. So I'm going to constrain that a little bit. Just going to set the maximum width of this particular div to XL. That's going to make it small. And then MX Auto is going to bring it in and justify it on both sides.

11:37 So let's hit Save and see how that looks. That's a lot more clean. OK, so now that we've got email going, it's up to you. You're going to go and add the first and last name fields to our form. You can check out how I did it in the next section.