Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
useForm
✓ import { useForm } from 'react-hook-form';
✗ const { useForm } = require('react-hook-form');
The primary hook for form management. Destructure directly from the import. CommonJS `require` is deprecated for modern React applications using ESM.
Controller
✓ import { Controller } from 'react-hook-form';
✗ import Controller from 'react-hook-form/dist/Controller';
Used for integrating controlled components from UI libraries (e.g., Material-UI, Ant Design). It acts as a wrapper. No default export.
useFieldArray
✓ import { useFieldArray } from 'react-hook-form';
✗ import { UseFieldArray } from 'react-hook-form';
Hook for managing dynamic forms (e.g., lists of items). Ensure correct capitalization.
FormProvider
✓ import { FormProvider } from 'react-hook-form';
Provides form context to deeply nested components, avoiding prop drilling.
This quickstart demonstrates a basic form using `useForm` with TypeScript, `register` for input binding and validation, `handleSubmit` for submission, and `formState.errors` for displaying validation feedback. It includes required fields, email pattern validation, and minimum age constraint.
import React from 'react';
import { useForm } from 'react-hook-form';
type FormData = {
firstName: string;
lastName: string;
email: string;
age: number;
};
export default function MyForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
defaultValues: {
firstName: '',
lastName: '',
email: '',
age: 18,
},
});
const onSubmit = (data: FormData) => {
console.log('Form Submitted:', data);
// In a real app, you would send this data to an API
// Example: fetch('/api/submit-form', { method: 'POST', body: JSON.stringify(data) });
alert(JSON.stringify(data, null, 2));
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 p-4 max-w-md mx-auto bg-white shadow-md rounded-lg">
<div>
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700">First Name:</label>
<input
id="firstName"
{...register('firstName', { required: 'First name is required' })}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
/>
{errors.firstName && <p className="mt-1 text-sm text-red-600">{errors.firstName.message}</p>}
</div>
<div>
<label htmlFor="lastName" className="block text-sm font-medium text-gray-700">Last Name:</label>
<input
id="lastName"
{...register('lastName', { required: 'Last name is required' })}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
/>
{errors.lastName && <p className="mt-1 text-sm text-red-600">{errors.lastName.message}</p>}
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">Email:</label>
<input
id="email"
type="email"
{...register('email', {
required: 'Email is required',
pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: 'Invalid email address' }
})}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
/>
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email.message}</p>}
</div>
<div>
<label htmlFor="age" className="block text-sm font-medium text-gray-700">Age:</label>
<input
id="age"
type="number"
{...register('age', { required: 'Age is required', min: { value: 18, message: 'Must be at least 18' } })}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
/>
{errors.age && <p className="mt-1 text-sm text-red-600">{errors.age.message}</p>}
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Submit
</button>
</form>
);
}
Errors
Common errors & fixes
TypeError: Cannot read properties of undefined (reading '_f')
This error often occurs when attempting to access internal `_f` property of a field that hasn't been properly registered with `react-hook-form` or has been unmounted. It can also happen when `Controller` or `useFieldArray` are misused or when the `control` object is not propagated correctly.
fixEnsure all input fields are correctly registered using `register` or wrapped with `Controller`. Verify that the `control` object from `useForm` is passed down to `Controller` and `useFieldArray` components, especially in nested contexts or when using `FormProvider`. Check for any conditional rendering that might unmount fields unexpectedly without proper unregistration handling.
formState.isValid incorrect on Controller re-mount
In specific scenarios, `formState.isValid` may not update correctly when a `Controller`-managed component re-mounts, leading to an inaccurate validation status of the form.
fixThis specific issue was addressed in `v7.72.1`. Upgrade to `react-hook-form@7.72.1` or a newer version to receive the fix.
Field array ghost elements with keepDirtyValues
Older versions of `useFieldArray` could sometimes produce 'ghost' elements or exhibit incorrect dirty state behavior when `keepDirtyValues` was enabled, especially after manipulation (append, remove, etc.).
fixThis was addressed in `v7.70.0`. Update to `react-hook-form@7.70.0` or a newer version to resolve issues with field array ghost elements and `keepDirtyValues`.
The first keystroke is not working, or input value is reset.
This typically happens if you are mistakenly using the `value` prop directly on an uncontrolled input that `react-hook-form` manages, instead of `defaultValue`. `React Hook Form` relies on uncontrolled inputs, where it reads the initial value from `defaultValue` and then tracks changes internally, not through React state for every keystroke.
fixReplace `value="..."` with `defaultValue="..."` for initial values on your inputs registered with `react-hook-form`. Avoid using `value` for managed inputs unless you are specifically working with controlled components via `Controller`.
Audit
Dependencies
reactrequiredPeer dependency required for React applications.