Efficient Form Handling in React: A Guide to React Hook Form with Yup

Bantawa Pawan
readytowork, Inc.
Published in
7 min readJul 16, 2023

Form handling is an essential aspect of building interactive web applications, and in the world of React, it becomes even more crucial to ensure a seamless user experience. React provides a powerful ecosystem for managing forms, but there’s no way around that we’ll need a reliable library to make it easier on ourselves.

React Hook Form is a popular library for handling forms in React. It offers a straightforward API that makes it easy to manage form states, validate inputs, and take form submissions. React Hook Form embraces the functional programming paradigm by utilizing hooks, which allows for more concise and readable code.

While React Hook Form provides a solid foundation for form handling and validation, it’s equally important to ensure that user inputs meet specific criteria. This is where Yup comes into play. Yup enables developers to define validation rules and schemas that can be seamlessly integrated with React Hook Form, resulting in a more robust and user-friendly form-handling experience.

In this article, we’ll explore React Hook Form and Yup, two popular tools for handling forms in React applications. We’ll look at how these tools can be used together to create powerful and reliable form-handling solutions.

Overview of React Hook Form and Yup

React Hook Form is a lightweight and flexible library for handling forms in React applications. It provides a comprehensive set of features and an intuitive API that simplifies form management and validation. By leveraging React’s hooks, React Hook Form offers a more functional approach to form handling, resulting in cleaner and more readable code.

Key Features of React Hook Form:

  • Simple API: React Hook Form offers a straightforward API that reduces boilerplate code and makes form handling more intuitive. It allows us to register inputs, track their values, and handle form submissions with ease.
  • Minimal Re-renders: One of the notable advantages of React Hook Form is its optimized performance. It leverages the concept of uncontrolled components, which minimizes unnecessary re-renders and improves the overall efficiency of form handling.
  • Validation Support: React Hook Form provides built-in support for form validation. It integrates seamlessly with validation libraries like Yup, allowing us to define validation rules and schemas for our form inputs.
  • Error Handling: Handling form errors is crucial for providing a smooth user experience. React Hook Form simplifies error handling by offering convenient methods to access and display validation errors associated with form inputs.

Now, let’s discuss the role of Yup in form validation:

Yup is a popular schema validation library that can be seamlessly integrated with React Hook Form. It allows us to define validation rules and schemas for our form inputs, ensuring that the data entered by users meets specific criteria.

By using Yup’s expressive API, we can define complex validation rules such as required fields, minimum and maximum lengths, number formats, email formats, and much more. Yup supports a wide range of validation methods and provides robust support for form validation in React applications.

When integrated with React Hook Form, Yup simplifies the process of validating form inputs. It automatically performs validation based on the defined schemas and provides detailed error messages that can be easily displayed to the users.

In the next section, we will dive deeper into the implementation details of React Hook Form and explore how Yup can be used to define validation schemas and streamline the form validation process.

Project overview and setup

In this project, we will be building a form that can be used to create and edit products. To achieve this, we will build a reusable Switch component that toggles between edit mode and create mode based on the user’s interactions with the form. In edit mode, the form fetches data from an API endpoint and pre-fills fields with information about the product being created or edited.

Setting up the project:

  • yarn create vite my-app --template react-swc-ts
  • cd my-app && yarn dev
  • yarn add yup react-hook-form yup

Optional

  • yarn add D tailwindcss
  • yarn tailwind init
  • Add the following in tailwind.config.js
content: [
“./src/**/*.{js,jsx,ts,tsx}”,
],
  • Remove the initial CSS and add the following in index.css
@tailwind base; 
@tailwind components;
@tailwind utilities;

./uitls/index.ts

export type Product = {
title?: string
description?: string
price?: number
brand?: string
}

App.tsx

import { useEffect, useState } from 'react'
import Form1 from './component/ReactHookForm'
import { Product } from './utils'
function App() {
const [enabled, setEnabled] = useState(false)
const [product, setProduct] = useState<Product>({})

useEffect(() => {
if (enabled) {
async function fetchData() {
try {
const response = await fetch('https://dummyjson.com/products/1')
const { title, description, price, brand } = await response.json()
setProduct({ title, description, price, brand })
} catch (error) {
console.log(error)
}
}

fetchData()
}
}, [enabled])
return (
<>
<div className="container mx-auto p-4">
<div className="relative flex flex-col items-center">
<div className="flex">
<label className="inline-flex relative items-center mr-5 cursor-pointer">
<input
type="checkbox"
className="sr-only peer"
checked={enabled}
readOnly
/>
<div
onClick={() => {
setEnabled(!enabled)
}}
className="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-green-300 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"
></div>
<span className="ml-2 text-sm font-medium text-gray-900">
Edit mode
</span>
</label>
</div>
</div>
</div>
<div className="container mx-auto p-4">
<div className="columns gap-10">
<Form1 productData={product} edit={enabled} />
</div>
</div>
</>
)
}

export default App

The App component has a checkbox labeled “Edit mode,” which when toggled fetches product data from an API and updates the form accordingly. The app component uses hooks like useState and uses the effect to manage state and perform side effects. The Form the component receives the product data and edit mode as props for rendering.

./components/Form/index.tsx

import React, { useEffect } from 'react'
import { Resolver, useForm } from 'react-hook-form'
import { yupResolver } from '@hookform/resolvers/yup'
import * as yup from 'yup'
import { Product } from '../../utils'

interface IFormProps {
productData?: Product
edit: boolean
}

const schema = yup.object().shape({
title: yup.string().required(),
description: yup.string().min(8).required(),
price: yup.number().min(1).max(1000).required(),
brand: yup.string().required(),
})
const Form: React.FC<IFormProps> = ({ productData, edit }) => {
const initialValues = {
title: '',
description: '',
price: 0,
brand: '',
}
const {
register,
handleSubmit,
setValue,
reset,
formState: { errors },
} = useForm({
defaultValues: productData ?? initialValues,
resolver: yupResolver(schema) as Resolver<Partial<Product>, any>,
})

useEffect(() => {
if (!edit) {
reset(initialValues)
}
}, [edit, reset, initialValues])

const onSubmit = (data: any) => {
alert('Submitted data' + JSON.stringify(data))
}

useEffect(() => {
setValue('title', productData?.title || '')
setValue('description', productData?.description || '')
setValue('price', productData?.price || 0)
setValue('brand', productData?.brand || '')
}, [productData, setValue])
return (
<div className="reactHookForm-Wrapper" onSubmit={handleSubmit(onSubmit)}>
<h1 className="text-2xl font-bold mb-4">React Hook Form</h1>
<form>
<div className="mb-4">
<label
htmlFor="title"
className="block text-black text-sm font-medium mb-2"
>
Name:
</label>
<input
type="text"
id="title"
className="w-full px-4 py-2 border border-gray-300 rounded focus:outline-none focus:border-black"
{...register('title')}
/>
{errors.title ? (
<span className="text-red-500 text-sm">{errors.title.message}</span>
) : null}
</div>
<div className="mb-4">
<label
htmlFor="description"
className="block text-black text-sm font-medium mb-2"
>
Description:
</label>
<textarea
id="description"
className="w-full px-4 py-2 border border-gray-300 rounded focus:outline-none focus:border-black"
{...register('description')}
></textarea>
{errors.description ? (
<span className="text-red-500 text-sm">
{errors.description.message}
</span>
) : null}
</div>
<div className="mb-4">
<label
htmlFor="price"
className="block text-black text-sm font-medium mb-2"
>
Price:
</label>
<input
type="number"
id="price"
className="w-full px-4 py-2 border border-gray-300 rounded focus:outline-none focus:border-black"
{...register('price')}
/>
{errors.price ? (
<span className="text-red-500 text-sm">{errors.price.message}</span>
) : null}
</div>
<div className="mb-4">
<label
htmlFor="brand"
className="block text-black text-sm font-medium mb-2"
>
Brand:
</label>
<select
id="brand"
className="w-full px-4 py-2 border border-gray-300 rounded focus:outline-none focus:border-black"
{...register('brand')}
value={productData?.brand || ''}
>
<option value="">Select a brand</option>
<option value="Apple">Apple</option>
<option value="Samsung">Samsung</option>
<option value="Oppo">Oppo</option>
</select>
{errors.brand ? (
<span className="text-red-500 text-sm">{errors.brand.message}</span>
) : null}
</div>
<button
type="submit"
className="bg-black text-white py-2 mb-2 px-4 rounded hover:bg-gray-900 focus:outline-none focus:bg-gray-900"
>
Submit
</button>
</form>
</div>
)
}
export default Form

A form component that uses the react-hook-form library, along with yup form validation. This component receives two props: productData, which represents the initial values for the form fields, and edit. A boolean flag indicates whether the form is in edit mode or not.

The component uses the useEffect hook to initialize and reset form values based on the productData and edit props. When the edit is false, the form is reset to its initial values.

The component renders a form with input fields for the product’s title, description, price, and brand. Each input field is registered react-hook-form using the register function. Validation errors are displayed based on the errors object provided by react-hook-form.

When the form is submitted, the onSubmit function is called, which simply displays an alert with the submitted form data.

Conclusion

In this article, we delved into the world of form handling in React and discovered the powerful combination of React Hook Form with Yup. Throughout our exploration, we covered various key points that highlight the advantages of using React Hook Form and Yup for our form-handling needs.

React Hook Form provides an intuitive and efficient solution for building forms in React. Its performance optimizations and streamlined API make it a standout choice. Additionally, the integration of Yup for schema-based validation adds an extra layer of reliability and flexibility to the form handling process.

Compared to other form-handling libraries like Formik, React Hook Form emerges as the clear winner. While Formik is a popular choice, React Hook Form offers distinct advantages that set it apart. One such feature is the ability to auto-focus and scroll to error fields without relying on third-party npm packages. In Formik, we would have needed packages like “formik-error-focus” or “focus-formik-error” to achieve the same functionality. React Hook Form’s built-in auto-focus and scroll-to-error-field feature simplifies the development process and enhances user experience.

In conclusion, React Hook Form combined with Yup presents a robust and efficient solution for form handling in React. The benefits are clear: improved performance, a more streamlined development experience, and enhanced user satisfaction. Take the leap and integrate React Hook Form with Yup in your next project — you won’t be disappointed.

Reference

https://blog.logrocket.com/react-hook-form-vs-formik-comparison/

--

--