Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

React Hook Form Cross-field Validation

September 30, 2020
reacttypescript

A cross-field validation rule is where the rule is dependent on multiple fields. This post covers how to implement these types of validation rules in React hook Form.

React Hook Form Cross-field Validation

An example

We have the following form that captures a low and high score:

type Scores = {
  low: number;
  high: number;
};

const ScoreForm = () => {
  const { register, handleSubmit } = useForm<Scores>();

  const onSubmit = (data: Scores) => {
    console.log("data", data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div className="field">
        <label htmlFor="low">Lowest score</label>
        <input type="number" id="low" name="low" ref={register} />
      </div>
      <div className="field">
        <label htmlFor="high">Highest score</label>
        <input type="number" id="high" name="high" ref={register} />
      </div>
      <button type="submit">Save</button>
    </form>
  );
};

This is a straightforward React Hook Form with no validation rules at the moment.

Adding simple validation

The scores are manditory and we want them to be between 0 and 10. Implementing these rules is super simple:

const ScoreForm = () => {
  const {
    register,
    handleSubmit,
    errors  } = useForm<Scores>();
  ...
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div className="field">
        <label htmlFor="low">Lowest score</label>
        <input
          type="number"
          id="low"
          name="low"
          ref={register({
            required: true,            min: 0,            max: 10          })}
        />
        {errors.low && errors.low.type === "required" && (          <div className="error">Your must enter a score.</div>        )}        {errors.low && errors.low.type === "min" && (          <div className="error">The score must be at least 0.</div>        )}        {errors.low && errors.low.type === "max" && (          <div className="error">The score can't be more than 10.</div>        )}      </div>
      <div className="field">
        <label htmlFor="high">Highest score</label>
        <input
          type="number"
          id="high"
          name="high"
          ref={register({
            required: true,            min: 0,            max: 10          })}
        />
        {errors.high && errors.high.type === "required" && (          <div className="error">Your must enter a score.</div>        )}        {errors.high && errors.high.type === "min" && (          <div className="error">The score must be at least 0.</div>        )}        {errors.high && errors.high.type === "max" && (          <div className="error">The score can't be more than 10.</div>        )}      </div>
      <button type="submit">Save</button>
    </form>
  );
};

The form fields now capture a low and high score between 0 and 10.

Adding cross-field validation

Can you spot a gap in the validation rules?

Yes, that’s right - the low score can be higher than the high score!

Let’s implement an additional validation rule to check the low score isn’t higher than the high score. This rule is an example of a cross-field validation rule because it is based on multiple fields - it is dependent on both the low and high score fields.

We can implement cross-field validation rules in React Hook Form with a custom validation rule.

First, we need to destructure the getValues from React Hook Form. This function allows us to access any field value on the form. We’ll need to use this in our custom validator function.

const {
  register,
  handleSubmit,
  errors,
  getValues,} = useForm<Scores>();

Now let’s implement the custom validation rule on the high score field:

<input
  type="number"
  id="high"
  name="high"
  ref={register({
    required: true,
    min: 0,
    max: 10,
    validate: () => Number(getValues("low")) <= Number(getValues("high"))  })}

We set the validate property in the register function’s object parameter to an inline validator function. A custom validator function for React Hook Form returns true if it passes and false if the rule fails. We use the getValues function to get the relevant field values in the validator function. The values of the fields are strings, so we use the Number constructor to convert them to numbers before doing the check.

Nice!

Let’s add the validation error message:

<input
  type="number"
  id="high"
  ...
/>
...
{errors.high && errors.high.type === "validate" && (  <div className="error">    The highest score can't be less than the lowest score.  </div>)}

The type property for a custom validation rule error is “validate”.

The validation error appears next to an invalid high score field when the form is submitted:

Validation error message

We could add this rule to the low score field as well, but it’s not necessary.

🏃 Play with the code

Wrap up

You can use a custom validation rule to implement cross-field validation in React Hook Form. The getValues function must be used to get access to the field values the rule is dependent on in the validator function.

Did you find this post useful?

Let me know by sharing it on Twitter.
Click here to share this post on Twitter

If you to learn more about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Find out more