Working with forms in React has always been a pain, and developers in the React ecosystem have constantly been working to streamline this tedious task. React Hook Form, one of the many libraries birthed out of this effort, has been proven effective at handling and validating forms. It also equips developers with other tools that improve the developer experience and make building forms more exciting.
From its inception, React Hook Form has been focused on not only serving the end user but also making things easier for developers.
In early April, the React team released a new version of the library, React Hook Form V7. The hot take from this version is that React Hook Form now supports strictly typed forms with the help of TypeScript, which bodes well for the library’s future.
Let’s take a closer look at these newly added features, including:
To demonstrate how forms can be strictly typed, we’ll need to create a sample project containing a form with some input fields. If you wish to follow along, you’ll need:
With those, we can now begin to set up our project.
Because we’ll be using type checking, we need to include TypeScript in project by running the following command in a terminal:
npx create-react-app hook-form --template typescript
This will create a React app named hook-form
with TypeScript integrated. It also makes a provision for a tsconfig.json
file so you are not required to create one.
Next, add react-hook-form
to the project:
npm i react-hook-form
With our example app set up, we’re ready to explore the new features of React Hook Form V7.
React Hook Form V7 introduces the benefit of static typing into React forms. This feature enables you to write efficient forms with fewer bugs.
To see strictly typed forms in action, open your App.tsx
file and replace its contents with the code below:
import React from 'react'; import { useForm } from 'react-hook-form' function App() { return ( <form> <label htmlFor="firstName">First Name</label> <input type="text" /> <label htmlFor="email">Email</label> <input type="email" /> <label htmlFor="phone">Phone Number</label> <input type="number" /> <label htmlFor="gender"></label> <input type="text" /> <input type="submit" value="submit"/> </form> ); } export default App;
Here we created a simple form with a few input fields, brought in the useForm
hook from react-hook-form
, and extracted the register
function from useForm
.
To achieve strictly typed forms, we’ll need to declare a type alias (we’ll call it FormValues
) above our function body and pass this into useForm
as a generic type:
import React from 'react'; import { useForm } from 'react-hook-form' function App() { const { register } = useForm<FormValues>() type FormValues = { firstName: string, email: string, phone: number, gender: any } return ( // ... ); } export default App;
This alias contains type specifications for the allowed types on each input field.
By passing it into useForm
, FormValues
now acts as a sort of schema to ensure that the name of any input field correlates with a key in the alias and the values entered into the input fields meet the type specifications.
With this in place, we can now use React Hook Form’s register
function to register the input fields we created. But before we register these fields, we should point out the syntax change to the register function in version 7.
Prior to this new release, the syntax used to register an input field in React Form Hook looked like this:
<input type="text" name="firstName" ref={register} /> >
The name of the input field we want react-hook-form
to return had to be stored in the HTML 5 name
attribute. Although this works fine, it has limitations.
To achieve strict type checking, React Hook V7 takes advantage of TypeScript 4.1 template literals. The register function has been modified to take the name of the input field (as its first argument) and use that to type-check against any alias passed into useForm
:
<input type="text" {...register('firstName') />
The following properties would be spread into the input:
const { onChange, blur, ref, name } = register('firstName')
Now let’s use the register function on each field, but we’ll intentionally make a typo on the email input to see how TypeScript reacts:
The benefits of this feature are undeniable. With strictly typed forms, you can be certain that the right data is being sent to the server by the user and bugs are removed from your code even before runtime. That’s a big win, if you ask me.
React Hook Form V7 brings reduces the package size by 13 percent. This is owed to the reduced resolver bundle size.
The table below shows the changes in the size of each file:
The React team went to great lengths to enhance the performance of the React Hook Form library, including introducing a new custom hook and improvements to some existing hooks in the API.
Let us go over these improvements in more detail.
The introduction of this new hook allows you to subscribe to individual form state, making each controlled input completely isolated in terms of rerender.
This means that when you need to subscribe to a part of a form, just that part is rerendered with any change to its state and not the entire application from the root level. This isolated render is also applicable to useController
and Controller
.
For example, say we want to be notified about any input field touched by the user. This is an example of component-level state. We can achieve this with useFormState
:
import * as React from "react"; import { useForm, useFormState } from "react-hook-form"; export default function App() { const { register, control } = useForm(); const { touchedFields } = useFormState({control}); console.log(touchedFields) return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName")} /> <input {...register("lastName")} /> <input type="submit" /> </form> ); } // Object logged to the console when user touches the firstName input field { firstName: true } // Object logged when user touches both fields { firstName: true, lastName: true }
Each time an input field is touched and an object logged to the console, the existing form state in the other input isn’t altered because the entire application isn’t rerendered.
For useFormState
to work, the control
object from useForm
has to be passed into it because this object contains methods for registering components into react-hook-form
.
Using this hook allows for better performance in large and complex form applications. Check out the full list of properties that can be subscribed to with useFormState
.
This custom hook saw no change in its syntax but had improvements behind the scenes. The data structure used in maintaining this hook was enhanced, enabling it to perform faster. It also allows improved focus management, which means you can target any input to focus as long as they expose their ref attribute.
useWatch
saw an improvement to its watch mechanism, making it easier to subscribe to form changes.
Like any other forward-thinking community in the tech world, the React team ended support for Internet Explorer 11 with React Hook Form V7. Microsoft itself will stop supporting IE 11 by August 17, 2021, so it’s safe to say we will no longer have to worry about IE 11 in the web development world.
Despite this change, React Hook Form V6 will still be maintained to support IE 11. We’ll discuss details about migrating to the latest version shortly.
If you’ve used React Hook Form previously, you know about the resolver function and probably know how to use it with validation packages such as Yup. React Hook Form V7 brings new improvements to this API by introducing a third argument to the codebase:
- resolver: (values: any, context?: object) => Promise<ResolverResult> | ResolverResult + resolver: ( + values: any, + context: object | undefined, + options: { + criteriaMode?: 'firstError' | 'all', + names?: string[], + fields: { [name]: Field } // Support nested fields + } + ) => Promise<ResolverResult> | ResolverResult
The new options
argument gives other libraries insight into the validation process and enables developers to write better custom logic to:
Credit to Joris, a core contributor to the React Hook Form project, for the examples.
React Hook Form V7 also supports other validation libraries, such as Zod, Vest, Joi, and Superstruct.
Asynchronous and synchronous validation options have also been added to the resolver
API, with async
being the default option. We can switch between the two by using the mode
argument:
useForm({ resolver: yupResolver(schema, schemaOptions, { mode: "async" | "sync"}) })
The async
option is available in all external validation libraries except Superstruct.
Bill Luo, the creator of React Hook Form, put together a detailed guide to help you migrate your existing V6 applications to React Hook Form V7. The guide includes changes you’ll need to make to your code and other important information you’ll need to make your existing apps work with React Hook Form V7.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]