avatarFilip Jerga, June 28, 2020

Form validation with Next.js/React part 1

Learn how to validate forms with "react hook form"

TODO: provide alt

Hello friends,

Video tutorial for this blog post you can find here: https://youtu.be/M7XYsrWorNo

Starting point for this tutorial: https://github.com/Jerga99/next-youtube-course/tree/v1.1

Complete code: https://github.com/Jerga99/next-youtube-course/commit/e31b92302b2878124dee09db32ec26d7535e611a

To validate a form we need some basic form template as an example. Here it is:

const PortfolioForm = () => {
  return (
    <form>
      <div className="form-group">
        <label htmlFor="title">Title</label>
        <input
          name="title"
          type="text"
          className="form-control"
          id="title"/>
      </div>

      <div className="form-group">
        <label htmlFor="city">Company</label>
        <input
          name="company"
          type="text"
          className="form-control"
          id="company"/>
      </div>

      <div className="form-group">
        <label htmlFor="city">Company Website</label>
        <input
          name="companyWebsite"
          type="text"
          className="form-control"
          id="companyWebsite"/>
      </div>

      <div className="form-group">
        <label htmlFor="street">Location</label>
        <input
          name="location"
          type="text"
          className="form-control"
          id="location"/>
      </div>

      <div className="form-group">
        <label htmlFor="street">Job Title</label>
        <input
          name="jobTitle"
          type="text"
          className="form-control"
          id="jobTitle"/>
      </div>

      <div className="form-group">
        <label htmlFor="description">Description</label>
        <textarea
          name="description"
          rows="5"
          type="text"
          className="form-control"
          id="description">
        </textarea>
      </div>
      <button
        type="submit"
        className="btn btn-primary">Create
      </button>
    </form>
  )
}

export default PortfolioForm;
PortfolioForm.js

Important part to note here is that every input element should have a "name" attribute. Thanks to the "name" data can be correctly assigned to the form object as you will see later.

Let's move on,

Now we need to install package for form validation called "react hook form"

In your terminals run: npm install --save react-hook-form
Now follow the comments in the code.

// import package on top of the file
import { useForm } from "react-hook-form";
// also Alert component from bootstrap
import { Alert } from 'react-bootstrap';

const PortfolioForm = () => {
  // useForm()
  // 1. register -> register input
  // 2. handleSubmit -> extract data from the form
  // 3. errors -> object containing errors
  const { register, handleSubmit, errors } = useForm();
  
  // function to output form data
  // we need to pass it to onSubmit of form element
  const onSubmit = formData => {
    alert(JSON.stringify(formData))
  }
  
  return (
    // pass onSubmit to handleSubmit of hook form
    // when button will be pressed you should see form data
    <form onSubmit={handleSubmit(onSubmit)}>
      <div className="form-group">
        <label htmlFor="title">Title</label>
        <input
          // Register input
          ref={register}
          name="title"
          type="text"
          className="form-control"
          id="title"/>
      </div>
      <div className="form-group">
        <label htmlFor="description">Description</label>
        <textarea
          // Register input
          ref={register}
          name="description"
          rows="5"
          type="text"
          className="form-control"
          id="description">
        </textarea>
      </div>
      <button
        type="submit"
        className="btn btn-primary">Create
      </button>
    </form>
  )
PortfolioForm.js

Please apply register to every input ref of your form. Very important is to also provide onSubmit event to the form. To onSubmit you will pass handleSubmit with callback function to get a data from the form.

So far so good, now we are able to get a data from the form. To validate inputs we need to provide built-in validators to register function.

Available build in validators are:

  • required
  • min
  • max
  • minLength
  • maxLength
  • pattern
  • validate

You can also create your own validator but about this later.

Now let's apply validators to title and city input

<div className="form-group">
  <label htmlFor="title">Title</label>
  <input
    // required, maxLength validators
    // you need to provide values to the inputs and max lenght of characters is 30
    ref={register({required: true, maxLength: 30 })}
    name="title"
    type="text"
    className="form-control"
    id="title"/>
    // Check for "title" errors
    { errors.title &&
    // if errors then display alert
      <Alert variant="danger">
        // if title error type is "required" display following message
        { errors.title?.type === "required" && <p>Title is required</p> }
        // if title error type is "maxLength" display following message
        { errors.title?.type === "maxLength" && <p>Max length of title is 30 characters!</p> }
      </Alert>
    }
  <div className="form-group">
    <label htmlFor="city">Company</label>
    <input
      ref={register({required: true, minLength: 5})}
      name="company"
      type="text"
      className="form-control"
      id="company"/>
      { errors.company &&
        <Alert variant="danger">
          { errors.company?.type === "required" && <p>Company is required</p> }
          { errors.company?.type === "minLength" && <p>Min length of title is 5 characters!</p> }
        </Alert>
      }
  </div>

  <div className="form-group">
    <label htmlFor="city">Company Website</label>
    <input
      ref={register({required: true, pattern: /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/})}
      name="companyWebsite"
      type="text"
      className="form-control"
      id="companyWebsite"/>
      { errors.companyWebsite &&
        <Alert variant="danger">
          { errors.companyWebsite?.type === "required" && <p>Website is required</p> }
          { errors.companyWebsite?.type === "pattern" && <p>Enter a valid url!</p> }
        </Alert>
      }
  </div>
</div>
PortfolioForm.js

Important part here is to register built-in validator. To display error message you need to check for a specific error located in the error object under the key of input "name", e.g: errors.title on the input of title

To check for a specific error you need to check for an error type, errors.title?.type==="required" whereas type is the key under which you registered validator on the input: ref={register({required: true, maxLength: 30})}

Now let's talk about custom validators. It's actually very simple to create one.

Validator is just a function that takes a value from the input and return true or false. When false is returned error is set on the errors object. When true is returned error is removed, which ultimately means that input is valid.

Let's create a simple validator which verifies input value is starting with an uppercased letter.

// true -> input is valid
// false -> input is NOT valid
const firstLetterUpper = iText => {
  // if there is no value in input return true, input is VALID
  if (!iText) { return true; }

  // if first letter is uppercased return true, input is VALID
  if (iText[0].toUpperCase() === iText[0]) {
    return true;
  }

  // in any other case return false, input is INVALID
  return false;
  // Or just simply return this
  // return iText[0].toUpperCase() === iText[0];
}
PortfolioForm.js

Register the validator on input.

ref={register({required: true, maxLength: 30, validate: { firstLetterUpper }})}
PortfolioForm.js

Now we are able to check for this error type:

{ errors.title?.type === "firstLetterUpper" && <p>First letter should be uppercased!</p> }
PortfolioForm.js

Already! This is how you can implement a simple validation with react hook form. In the second part of form validation you are going to learn how to validate more complicated components as for example "date pickers".

If you would like to see more of my courses please visit: https://eincode.com

Best,

Filip