Built-in Validation Rules
React Hook Form ships with built-in validation rules that cover the vast majority of real-world form requirements. You attach them as a second argument to register, and RHF handles the rest -- checking values, populating errors, and preventing submission when something fails.
Passing Rules to Register
You pass validation rules as the second argument to register:
<input {...register("email", { required: "Email is required" })} />
The string you pass becomes the error message in formState.errors.email.message. Let's go through every built-in rule.
required
The most common rule. It ensures the field has a value before the form can submit.
<input {...register("name", { required: true })} />
<input {...register("name", { required: "Name is required" })} />
When you pass true, RHF marks the field as invalid but errors.name.message is undefined. When you pass a string, that string becomes the message directly. Always use the string form so your error display logic stays simple.
min and max
These apply to number inputs. They validate that the numeric value falls within a range.
<input
type="number"
{...register("age", {
min: { value: 18, message: "Must be at least 18" },
max: { value: 120, message: "Must be 120 or under" },
})}
/>
You can pass a plain number -- min: 18 -- but then you lose the custom error message, the same tradeoff as required: true.
minLength and maxLength
These apply to string inputs. They validate the number of characters in the field.
<input
{...register("username", {
minLength: { value: 3, message: "Must be at least 3 characters" },
maxLength: { value: 20, message: "Must be 20 characters or fewer" },
})}
/>
Remember: min/max check numeric value, minLength/maxLength check string length. Don't mix them up.
pattern
The pattern rule validates against a regular expression -- perfect for email addresses, phone numbers, or any structured input.
<input
{...register("email", {
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "Enter a valid email address",
},
})}
/>
The value property is the regex, and message is the error text when the input doesn't match. Any valid JavaScript regex works -- /^[A-Za-z]+$/i for letters only, /^\d{5}$/ for a zip code, etc.
validate
The validate rule lets you write custom validation functions. We'll cover this in depth next lesson, but here's the idea:
<input
{...register("username", {
validate: (value) => value !== "admin" || "Username not available",
})}
/>
The function receives the field value and returns true if valid, or a string error message if invalid.
Complete Example
Rules compose naturally -- stack as many as you need on a single field, and RHF evaluates them in order. The first rule that fails produces the error. Here's a registration form that combines multiple rules per field with inline error display:
import { useForm } from "react-hook-form";
function RegistrationForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: { name: "", email: "", age: undefined as number | undefined },
});
const onSubmit = (data: { name: string; email: string; age: number }) => {
console.log("Submitted:", data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="name">Name</label>
<input
id="name"
{...register("name", {
required: "Name is required",
minLength: { value: 2, message: "Name must be at least 2 characters" },
})}
/>
{errors.name && <p style={{ color: "red" }}>{errors.name.message}</p>}
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
{...register("email", {
required: "Email is required",
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "Enter a valid email address",
},
})}
/>
{errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}
</div>
<div>
<label htmlFor="age">Age</label>
<input
id="age"
type="number"
{...register("age", {
required: "Age is required",
valueAsNumber: true,
min: { value: 18, message: "You must be at least 18 years old" },
})}
/>
{errors.age && <p style={{ color: "red" }}>{errors.age.message}</p>}
</div>
<button type="submit">Register</button>
</form>
);
}
export default RegistrationForm;
The name field requires at least 2 characters. The email field must match an email pattern. The age field must be at least 18 -- and valueAsNumber ensures the submitted data is a number, not a string. Every rule uses the string form, so errors.[field].message is always ready to render.
The built-in rules — required, min, max, minLength, maxLength, and pattern — handle most common validation scenarios without extra code. Always use the object form with a message property so your errors are descriptive. For anything these rules can't express, the validate option lets you write custom functions.