No one likes creating and recreating complex forms with validation, including React developers.

When building forms in React, you have to use a form library that provides many convenient tools and doesn’t require much code.

Based on both utility and simplicity, the most ideal React form library for an application is the React-hook-form.

Let’s take a look at how to use React-hook-form in your own projects to build rich, distinctive forms for your React application.

The installation

Let’s discuss a typical use case: a user registers with our application.

For our registration form, we will provide three inputs for any new user’s username, password, and email:

import React from "react";

const styles = {
  container: {
    width: "80%".margin: "0 auto",},input: {
    width: "100%",}};export default function Signup() {
  return (
    <div style={styles.container}>
      <h4>Sign up</h4>
      <form>
        <input placeholder="Username" style={styles.input} />
        <input placeholder="Email" style={styles.input} />
        <input placeholder="Password" style={styles.input} />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Copy the code

Once the React project is up and running, we’ll start by installing the React-hook-form library:

npm i react-hook-form
Copy the code

Using useForm hook

To use react-hook-form, we simply call the useForm hook.

When we do that, we’ll get an object from which we’ll deconstruct the Register property.

Register is a function that we need to attach to each input as ref.

function App() {
  const { register } = useForm();

  return (
    <div style={styles.container}>
      <h4>Signup</h4>
      <form>
        <input ref={register} placeholder="Username" style={styles.input} />
        <input ref={register} placeholder="Email" style={styles.input} />
        <input ref={register} placeholder="Password" style={styles.input} />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Copy the code

The register function will accept the value entered by the user in each input to validate it. Register will also pass each value to a function that will be called when the form is submitted, which we’ll discuss below.

For register to work, we need to provide an appropriate name attribute for each input. For example, for username input, its name is “username”.

The reason for this is that when we submit the form, we get all the input values on a single object. The properties of each object will be named according to the input name properties we specify. The code is as follows:

function App() {
  const { register } = useForm();

  return (
    <div style={styles.container}>
      <h4>My Form</h4>
      <form>
        <input
          name="username"
          ref={register}
          placeholder="Username"
          style={styles.input}
        />
        <input
          name="email"
          ref={register}
          placeholder="Email"
          style={styles.input}
        />
        <input
          name="password"
          ref={register}
          placeholder="Password"
          style={styles.input}
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Copy the code

Submit the form using handleSubmit

To handle submitting the form and receiving input data, we’ll add an onSubmit to the form element and connect it to a local function of the same name:

function App() {
  const { register } = useForm();

  function onSubmit() {}

  return (
    <div style={styles.container}>
      <h4>My Form</h4>
      <form onSubmit={onSubmit}>
        <input
          name="username"
          ref={register}
          placeholder="Username"
          style={styles.input}
        />
        <input
          name="email"
          ref={register}
          placeholder="Email"
          style={styles.input}
        />
        <input
          name="password"
          ref={register}
          placeholder="Password"
          style={styles.input}
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Copy the code

From useForm, we’ll get a function called HandlesSubmit and wrap it around onSubmit as a higher-order function.

The HandlesSubmit function will be responsible for collecting all the data entered into each input, and we will receive an object named Data in onSubmit.

Now, if we use console.log(data), we can see what we entered in each input of the same property:

function App() {
  const { register, handleSubmit } = useForm();

  function onSubmit(data) {
    console.log(data); 
    // { username: 'test', email: 'test', password: 'test' }
  }

  return (
    <div style={styles.container}>
      <h4>Signup</h4>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input
          name="username"
          ref={register}
          placeholder="Username"
          style={styles.input}
        />
        <input
          name="email"
          ref={register}
          placeholder="Email"
          style={styles.input}
        />
        <input
          name="password"
          ref={register}
          placeholder="Password"
          style={styles.input}
        />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
Copy the code

Form validation

Validating the form and adding constraints for each input value is simple — we just need to pass the information to the Register function.

Register takes an object that contains a number of properties that tell Register how to validate a given input.

The first attribute is required. By default, it is set to false, but we can set it to true to ensure that the form is not submitted without being filled out.

We want the username value to be required, and we want the user’s username to be longer than 6 characters but less than 24 characters.

To apply this validation, we can set the minLength constraint to 6, but maxLength should be 20:

<input
  name="username"
  ref={register({
    required: true.minLength: 6.maxLength: 20,
  })}
  style={styles.input}
  placeholder="Username"
/>
Copy the code

If we use numbers for this input (assuming the input is about a person’s age), we will use the attributes min and Max instead of minLength and maxLength.

Regular expression

Next, we can provide a Regex regular expression pattern if we wish.

If we want the username to contain only upper and lower case characters, we can use the following regular expression, which allows custom validation:

<input
  name="username"
  ref={register({
    required: true.minLength: 6.maxLength: 20.pattern: /^[A-Za-z]+$/i,
  })}
  style={styles.input}
  placeholder="Username"
/>
Copy the code

validate

Finally, validate, a custom function that allows us to access the input values. Validate allows us to provide our own logic to determine whether it is valid (by returning a Boolean value of true or false).

For E-mail here, we also want it to be required and valid E-mail. To verify this, we can pass input to a function from a library validator named isEmail.

Returns true if an email was entered. Otherwise, wrong:

<input
  name="email"
  ref={register({
    required: true.validate: (input) = > isEmail(input), // returns true if valid
  })}
  style={styles.input}
  placeholder="Email"
/>
Copy the code

For password register, we set required to true, minLength to 6, and maxLength not set:

<input
  name="password"
  ref={register({
    required: true.minLength: 6
  })}
  style={styles.input}
  placeholder="Password"
/>
Copy the code

Error message

Now, if the input in the form is invalid, we do not tell the user that there is any error. We need to give them feedback to fix the values they provide.

When one of the inputs is invalid, the form data is not submitted (onSubmit is not called). In addition, the first input with an error will automatically focus, and it will not give the user any detailed feedback about what happened.

Instead of just not submitting the form, we can get an errors object from useForm.

Just like the data function we obtained in onSubmit, Errors contains attributes corresponding to each input name if it has an error.

For our example, we can add a condition for each input and say that if there is an error, we will set the borderColor to red.

function App() {
  const { register, handleSubmit, errors } = useForm();

  function onSubmit(data) {
    console.log(data);
  }

  return (
    <div style={styles.container}>
      <h4>My Form</h4>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input
          name="username"
          ref={register({
            required: true.minLength: 6.maxLength: 20.pattern:/ ^ [A-Za-z] + $/i})},style={{ . styles.input.borderColor: errors.username&&. ""red"}}placeholder="Username"
        />
        <input
          name="email"
          ref={register({
            required: true.validate: (input) = >isEmail(input), })} style={{ ... styles.input, borderColor: errors.email && "red" }} placeholder="Email" /><input
          name="password"
          ref={register({
            required: true.minLength: 6})},style={{ . styles.input.borderColor: errors.password&&. ""red"}}placeholder="Password"
        />
        <button type="submit" disabled={formState.isSubmitting}>
          Submit
        </button>
      </form>
    </div>
  );
}
Copy the code

Authentication mode

You’ll notice that by default, the Errors object is updated only when the form is submitted. The default validation is performed only when the form is submitted.

We can change this by passing an object to useForm. We can set the mode when we need to perform validation :onBlur, onChange, or onSubmit.

OnBlur will make the validation run whenever the user ‘loses focus’ or clicks away from the input. OnChange validates when the user enters and onSubmit validates when the form is submitted.

For our form, let’s select onBlur:

const { register, handleSubmit, errors } = useForm({
  mode: "onBlur"});Copy the code

Note that there are other ways to set up and clear errors manually (setError and clearError). For example, in some cases you want it to create a different error or clear an error in onSubmit, you can use these methods.

How do I disable formState for forms

The last value we can get from the useForm hook is formState.

It gives us important information, such as when something was entered and when the form was submitted.

So, if you want to disable the form’s button to make sure the form hasn’t been submitted more than once, we can set the disable to formState.isSubmitted.

When the form is submitted, it is disabled until validation is complete by running the onSubmit function.

conclusion

I hope this article has shown you how to make it easier to create functional forms in your React application.

There are many other features related to the React-hook form that I haven’t covered here. Click here, and the official documentation should cover any use case you can think of.