{"id":11717,"library":"react-hook-form","title":"React Hook Form","description":"React Hook Form is a high-performance, flexible, and extensible forms library for React applications, currently stable at version 7.72.1. It leverages an uncontrolled component approach, minimizing re-renders and improving performance by isolating component updates from form state changes. The library maintains a rapid release cadence, frequently delivering patches and minor versions, with a beta for version 8 already available. Key differentiators include its focus on performance, small bundle size, seamless integration with native HTML form validation, and robust TypeScript support for enhanced type safety across form values and errors. It provides essential hooks like `useForm` for comprehensive form management, `Controller` for integrating with external controlled components, and `useFieldArray` for dynamic lists of inputs, making it suitable for a wide spectrum of form complexities.","status":"active","version":"7.72.1","language":"javascript","source_language":"en","source_url":"https://github.com/react-hook-form/react-hook-form","tags":["javascript","react","hooks","form","forms","form-validation","validation","typescript","react-hooks"],"install":[{"cmd":"npm install react-hook-form","lang":"bash","label":"npm"},{"cmd":"yarn add react-hook-form","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-hook-form","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency required for React applications.","package":"react","optional":false}],"imports":[{"note":"The primary hook for form management. Destructure directly from the import. CommonJS `require` is deprecated for modern React applications using ESM.","wrong":"const { useForm } = require('react-hook-form');","symbol":"useForm","correct":"import { useForm } from 'react-hook-form';"},{"note":"Used for integrating controlled components from UI libraries (e.g., Material-UI, Ant Design). It acts as a wrapper. No default export.","wrong":"import Controller from 'react-hook-form/dist/Controller';","symbol":"Controller","correct":"import { Controller } from 'react-hook-form';"},{"note":"Hook for managing dynamic forms (e.g., lists of items). Ensure correct capitalization.","wrong":"import { UseFieldArray } from 'react-hook-form';","symbol":"useFieldArray","correct":"import { useFieldArray } from 'react-hook-form';"},{"note":"Provides form context to deeply nested components, avoiding prop drilling.","symbol":"FormProvider","correct":"import { FormProvider } from 'react-hook-form';"}],"quickstart":{"code":"import React from 'react';\nimport { useForm } from 'react-hook-form';\n\ntype FormData = {\n  firstName: string;\n  lastName: string;\n  email: string;\n  age: number;\n};\n\nexport default function MyForm() {\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n  } = useForm<FormData>({\n    defaultValues: {\n      firstName: '',\n      lastName: '',\n      email: '',\n      age: 18,\n    },\n  });\n\n  const onSubmit = (data: FormData) => {\n    console.log('Form Submitted:', data);\n    // In a real app, you would send this data to an API\n    // Example: fetch('/api/submit-form', { method: 'POST', body: JSON.stringify(data) });\n    alert(JSON.stringify(data, null, 2));\n  };\n\n  return (\n    <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-4 p-4 max-w-md mx-auto bg-white shadow-md rounded-lg\">\n      <div>\n        <label htmlFor=\"firstName\" className=\"block text-sm font-medium text-gray-700\">First Name:</label>\n        <input\n          id=\"firstName\"\n          {...register('firstName', { required: 'First name is required' })}\n          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\"\n        />\n        {errors.firstName && <p className=\"mt-1 text-sm text-red-600\">{errors.firstName.message}</p>}\n      </div>\n\n      <div>\n        <label htmlFor=\"lastName\" className=\"block text-sm font-medium text-gray-700\">Last Name:</label>\n        <input\n          id=\"lastName\"\n          {...register('lastName', { required: 'Last name is required' })}\n          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\"\n        />\n        {errors.lastName && <p className=\"mt-1 text-sm text-red-600\">{errors.lastName.message}</p>}\n      </div>\n\n      <div>\n        <label htmlFor=\"email\" className=\"block text-sm font-medium text-gray-700\">Email:</label>\n        <input\n          id=\"email\"\n          type=\"email\"\n          {...register('email', { \n            required: 'Email is required',\n            pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i, message: 'Invalid email address' }\n          })}\n          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\"\n        />\n        {errors.email && <p className=\"mt-1 text-sm text-red-600\">{errors.email.message}</p>}\n      </div>\n\n      <div>\n        <label htmlFor=\"age\" className=\"block text-sm font-medium text-gray-700\">Age:</label>\n        <input\n          id=\"age\"\n          type=\"number\"\n          {...register('age', { required: 'Age is required', min: { value: 18, message: 'Must be at least 18' } })}\n          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\"\n        />\n        {errors.age && <p className=\"mt-1 text-sm text-red-600\">{errors.age.message}</p>}\n      </div>\n\n      <button\n        type=\"submit\"\n        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\"\n      >\n        Submit\n      </button>\n    </form>\n  );\n}","lang":"typescript","description":"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."},"warnings":[{"fix":"For `register`, pass the input's `ref` directly. For `useFieldArray`, update usage from `id` to `key` and remove `keyName`. Refer to the v8 migration guide for detailed instructions on `setValue` changes as well.","message":"Version 8.0.0-beta.1 introduces breaking changes: `register` now expects an input `ref` directly instead of a partial object. The `useFieldArray` hook has renamed its `id` property to `key` and removed the `keyName` prop.","severity":"breaking","affected_versions":">=8.0.0-beta.1"},{"fix":"Upgrade to the latest patch version (7.72.1 or newer) immediately to ensure all known security fixes are applied. Monitor official `react-hook-form` releases and security advisories for further guidance.","message":"Critical RCE (Remote Code Execution) vulnerabilities (CVE-2025-55182, CVE-2025-55183, CVE-2025-55184, CVE-2025-67779) were listed in v7.69.0. While the specific impact on `react-hook-form` is not fully detailed in the provided excerpt, such CVEs in dependencies or related ecosystems (like React Server Components as mentioned in general CVE discussions) can indicate severe security risks. Users should prioritize updating and reviewing their dependency tree.","severity":"breaking","affected_versions":"7.69.0"},{"fix":"Upgrade to `react-hook-form@7.72.1` or newer. If on an older version, manually manage dirty state for complex scenarios or ensure `defaultValues` are correctly structured and used.","message":"When using `setValue` with `shouldDirty` (or when relying on `isDirty` with numeric string keys in `defaultValues`), older versions may incorrectly mark unrelated fields as dirty or fail to correctly track dirtiness. This can lead to unexpected form state behavior.","severity":"gotcha","affected_versions":"<7.72.1"},{"fix":"Always pass a complete `defaultValues` object to the `useForm` hook, ensuring it reflects the initial state of all registered inputs.","message":"Providing `defaultValues` to `useForm` is crucial for `isDirty` and `dirtyFields` to work correctly. Without `defaultValues`, the library has no baseline to compare against, leading to `isDirty` always being `false` or `dirtyFields` being empty even after user interaction.","severity":"gotcha","affected_versions":">=7.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure 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.","cause":"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.","error":"TypeError: Cannot read properties of undefined (reading '_f')"},{"fix":"This specific issue was addressed in `v7.72.1`. Upgrade to `react-hook-form@7.72.1` or a newer version to receive the fix.","cause":"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.","error":"formState.isValid incorrect on Controller re-mount"},{"fix":"This 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`.","cause":"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.).","error":"Field array ghost elements with keepDirtyValues"},{"fix":"Replace `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`.","cause":"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.","error":"The first keystroke is not working, or input value is reset."}],"ecosystem":"npm"}