Skip to main content

React Hook Form Part 2: Yup Schema Validation

React Hook Form integrates seamlessly with schema validation libraries like Yup using the @hookform/resolvers package. This approach decouples validation logic from JSX, making forms more readable, reusable, and maintainable. Yup lets you define validation rules declaratively in a schema object, then connect it to your form with a single resolver prop.

Key Takeaways

  • Schema validation (Yup) keeps validation logic separate from your component, making code cleaner and more maintainable
  • The @hookform/resolvers package bridges React Hook Form with Yup, Zod, Joi, and other schema libraries
  • Yup supports chained validation rules (required(), min(), max(), email(), oneOf()) for complex field validation
  • A validation schema is defined once and reused across multiple forms, reducing code duplication

Core Concepts of Schema Validation with Yup

While React Hook Form has its own built-in validation, it also integrates seamlessly with schema validation libraries like Yup. This approach allows you to define your validation rules in a separate schema object, keeping your component code clean and focused on rendering.

Yup is a JavaScript schema builder for value parsing and validation. It lets you chain validation methods declaratively. The @hookform/resolvers package is a small utility that connects any schema validation library (Yup, Zod, Joi, etc.) to React Hook Form.

This separation of concerns means your component is responsible for rendering the form, while the schema handles all validation logic. If validation rules change, you update the schema once—no need to touch the JSX.

Implementation and Walkthrough

Let us build a registration form with React Hook Form and Yup validation.

Installation

First, install React Hook Form, Yup, and the resolver:

npm install react-hook-form yup @hookform/resolvers

Creating the Validation Schema with Yup

Create a validationSchema.js file to define your validation rules. This schema is the single source of truth for all validation logic in the form.

// validationSchema.js
import * as yup from 'yup';

export const validationSchema = yup.object({
firstName: yup.string()
.max(15, 'Must be 15 characters or less')
.required('Required'),
lastName: yup.string()
.max(20, 'Must be 20 characters or less')
.required('Required'),
email: yup.string()
.email('Invalid email address')
.required('Required'),
password: yup.string()
.min(8, 'Password must be at least 8 characters')
.required('Required'),
confirmPassword: yup.string()
.oneOf([yup.ref('password'), null], 'Passwords must match')
.required('Required'),
}).required();

Schema Breakdown:

  • yup.string() — Validates that the field is a string
  • .required('Required') — Field cannot be empty; shows the error message "Required"
  • .email('Invalid email address') — Validates email format
  • .min(8, '...') — String must be at least 8 characters
  • .max(15, '...') — String cannot exceed 15 characters
  • .oneOf([yup.ref('password'), null], '...') — Value must match another field (password confirmation)

The schema defines the shape and rules for valid data. When Yup validates, it returns either the validated data or an error object.

Building the Form with Yup Resolver

Now, connect the schema to your form using the yupResolver:

import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { validationSchema } from './validationSchema';

function RegistrationForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(validationSchema),
});

const onSubmit = (data) => {
console.log('Valid data:', data);
// Send to server, update database, etc.
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="firstName">First Name</label>
<input id="firstName" {...register('firstName')} />
{errors.firstName && <p style={{color: 'red'}}>{errors.firstName?.message}</p>}
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<input id="lastName" {...register('lastName')} />
{errors.lastName && <p style={{color: 'red'}}>{errors.lastName?.message}</p>}
</div>
<div>
<label htmlFor="email">Email Address</label>
<input id="email" type="email" {...register('email')} />
{errors.email && <p style={{color: 'red'}}>{errors.email?.message}</p>}
</div>
<div>
<label htmlFor="password">Password</label>
<input id="password" type="password" {...register('password')} />
{errors.password && <p style={{color: 'red'}}>{errors.password?.message}</p>}
</div>
<div>
<label htmlFor="confirmPassword">Confirm Password</label>
<input id="confirmPassword" type="password" {...register('confirmPassword')} />
{errors.confirmPassword && <p style={{color: 'red'}}>{errors.confirmPassword?.message}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
}

export default RegistrationForm;

Step-by-Step Code Breakdown:

  1. import { yupResolver } from '@hookform/resolvers/yup'; — Import the Yup resolver
  2. resolver: yupResolver(validationSchema) — Pass the yupResolver to the useForm hook, with your validation schema as an argument. This tells React Hook Form how to validate
  3. {...register('firstName')} — The register function connects each input to React Hook Form's state and validation
  4. {errors.firstName?.message} — Display the Yup error message for this field. The optional chaining (?.) safely accesses the message if the error exists
  5. handleSubmit(onSubmit) — Prevents default form submission, validates using the Yup schema, and calls onSubmit only if validation passes

When a user submits the form, React Hook Form runs the Yup validation against all fields. If validation fails, error messages appear. If validation passes, onSubmit receives the valid data.

Advantages of Schema Validation with Yup

  • Declarative: Validation rules are explicit and easy to read as a single object
  • Reusable: Define the schema once, use it in multiple forms or functions
  • Composable: Combine and extend schemas with yup.extend() and object composition
  • Server Sync: Use the same schema on the server (if using JavaScript on the backend) to ensure consistency
  • Dynamic Rules: Change validation based on other field values using when()

Frequently Asked Questions

What is the difference between Yup and other validation libraries like Zod?

Yup and Zod both define schemas declaratively, but Zod is more recent and offers better TypeScript support with inference. Yup is more established with a larger ecosystem. Both work with @hookform/resolvers. For this tutorial, Yup is sufficient; choose Zod if you want stronger TypeScript integration.

Can I validate custom logic that Yup does not provide?

Yes. Yup provides .test() to write custom validation logic. For example: .test('even', 'Must be even', value => value % 2 === 0). You can also use .test('async', ...) for async validation (e.g., checking if an email is already registered).

How do I show validation errors in a user-friendly way?

The error message comes from your Yup schema (the second argument to validation methods, e.g., .required('Required')). Render it with {errors.fieldName?.message}. Style the error paragraph with CSS to make it red or highlighted. You can also disable the submit button while there are errors: <button disabled={Object.keys(errors).length > 0}>Submit</button>.

Can I use Yup and React Hook Form together without knowing Yup syntax?

Yes, you can use simple validation with React Hook Form's built-in rules first. Once comfortable, switch to Yup for more complex validation. The @hookform/resolvers package makes the transition seamless.

Use Yup's .oneOf([yup.ref('password'), null], 'Passwords must match') to compare the current field to another field. The yup.ref() function references another field in the schema by name.

Further Reading

Glossary

  • Yup: A JavaScript schema validation library for value parsing and validation. Schemas define the shape and rules for valid data.
  • @hookform/resolvers: A package that provides adapters (resolvers) for using schema validation libraries (Yup, Zod, Joi) with React Hook Form.
  • Schema: A representation of the structure and validation rules for data. In Yup, a schema is a JavaScript object defining field types and constraints.
  • Resolver: A function passed to React Hook Form's useForm that validates form data against a schema and returns errors if validation fails.