ProNextJS
    Level up with Forms Management with Next.js App Router

    I respect your privacy. Unsubscribe at any time.

    This is a free tutorial

    In exchange for your email address, you'll get full access to this and other free ProNextJS tutorials.

    Why? First and foremost, your inbox allows us to directly communicate about the latest ProNextJS material. This includes free tutorials, NextJS Tips, and periodic update about trends, tools, and NextJS happenings that I'm excited about.

    In addition to the piles of free NextJS content, you'll get the earliest access and best discounts to the paid courses when they launch.

    There won't be any spam, and every email you get will have an unsubscribe link.

    If this sounds like a fair trade, let's go!

    lesson

    Client-Side Form Validation

    Jack HerringtonJack Herrington

    Adding the remaining first and last name fields to the form is a similar process to adding the email field. Copying and pasting the email field and modifying the name and description will do the trick:

    // inside RegistrationForm.tsx
      return (
        <Form {...form}>
          <form
            className="space-y-8"
          >
              <FormField
                control={form.control}
                name="first"
                render={({ field }) => (
                  <FormItem className="w-full">
                    <FormLabel>First Name</FormLabel>
                    <FormControl>
                      <Input placeholder="" {...field} />
                    </FormControl>
                    <FormDescription>Your first name.</FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="last"
                render={({ field }) => (
                  <FormItem className="w-full">
                    <FormLabel>Last Name</FormLabel>
                    <FormControl>
                      <Input placeholder="" {...field} />
                    </FormControl>
                    <FormDescription>Your last name.</FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
              ...
    

    At this point the form fields will be displayed one below the other.

    In order to display the first and last name fields side by side, we can wrap them in a div that uses Tailwind's Flexbox classes to put them side by side with a gap in between:

    <form className="space-y-8">
    <div className="flex gap-2">
      {/* FormFields for first and last name */}
    </div>
      {/* FormField for email */}
    

    Now the form fields for the first and last name will be displayed side by side above the email field:

    The current form

    However, when the submit button is clicked, nothing happens. This is because the handleSubmit function isn't connected to the actual onSubmit event that gets triggered when the submit button is clicked.

    Writing the onSubmit Function

    Inside of RegistrationForm.tsx, above the return, create a new async onSubmit function. The function's data will be given the type of our schema, thanks to the z.infer trick. This ensures we don't have to perform any manual synchronization since any alterations in the schema will correspondingly modify the rest. For the time being, the function will console log the data that comes in:

    const onSubmit = async (data: z.infer<typeof schema>) => {
      console.log(data);
    }
    

    Now it's time to establish a connection with the form.

    Connecting the onSubmit Function to the Form

    Inside of the <form> being returned from the component, add an onSubmit prop that will be given the form.handleSubmit(onSubmit) function. This will ensure that all validations are applied:

    // inside of the `return` statement
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
        ...
    

    With the onSubmit connected, let's give the form a test run.

    Testing the Form

    First we'll keep all of the fields empty and hit the submit button. As expected, everything turns out to be invalid, and checking the console reveals that no output has been generated.

    Invalid field messages are displayed

    Next, fill out the form with some random data and hit submit again. This time around, we can see in the console that onSubmit has been invoked with the data, and everything has been trimmed and validated. This is a clear indication that our client-side validation is working perfectly.

    The form is valid and we can see data in the console

    And there you have it, folks! Client-side validation can be that simple.

    In the next step, we'll look at how you can post this to the server and validate it there.

    Transcript

    All right, let's go take a look at the code and we'll go and add in our first and last name fields. To do that, I'll bring in two additional form fields and either basically just copy and paste of the original email field where I just changed the name of the field and also the description. Let's take a look.

    It looks nice, but I do want first and last name set side by side, so I'm going to add a little bit more tailwind. I'm just going to wrap this form field in a div that has a flex box that's going to go in by default, put them side by side with a little gap in the middle. Magnifique.

    But does it validate? Let's give it a try. If I hit submit here, nothing happens. So what's going on? Well, what's happening is that we actually haven't connected the handle submit to the actual on submit, which is triggered when you click on submit here.

    So let's go and create an on submit and then connect it to our form. So I'll create a new on submit function right at the top here. And what's going to be passed to on submit is again, the type of our schema. So we're going to use that Z infer trick again to get the type of our schema. And that means that we don't actually have to synchronize any of this.

    Anytime we change the schema, basically all of this is going to change automatically for us, which is fantastic. Now, at this point, all it's going to do is just console log out the data that comes in. So now we just have to connect it to the form. Do that down in the form field. We add an on submit prop. We first pass the on submit to handle submit.

    That's going to wrap it so that all of those validations are applied. So let's hit save and say we go, all right, let's just keep it empty, hit submit and everything's invalid. That's fantastic. Let's see in the console. Did I get anything? Nope. All right, let's go. Okay. Hit submit.

    And now I get on submit called with all the data and everything's been trimmed and validated. Fantastic. So now we've got client side validation working and wow, that was super easy, right? Let's actually post this to the server and see how to go and validate it on the server