NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #750 most downloaded on npm
Performant, flexible and extensible forms library for React Hooks
Last release 8 days ago
26 Sep 2026
Ships on a steady schedule
a new release about every 2 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
8 years old
1100 releases · first in 2019
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
One column per quarter.
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
🦾 Syncs v8 beta with the latest master branch, bringing over recent v7 bug fixes, performance improvements, refactors, and DX updates.
⚠️ Breaking change: pass input ref instead of partial
🦭 Integrated the latest updates and fixes from Version 7
useFieldArray
id to keykeyName props removed for useFieldArrayconst { fields, append } =useFieldArray()
// data will be part of the submission
append({
key: 'key', // does not affect render key
id: 'key', // does not affect render key
})
field[0].key // will still be uniq id for re-render
<Watch /> rename names to namewatch call back api removedsetValue no longer direct update useFieldArray, instead use replace api
https://react-hook-form.com/docs/useform/setvaluenpm i react-hook-form@8.0.0-beta.1
⬆️ Promoted v8 from alpha to beta stability 🦭 Integrated the latest updates and fixes from Version 7
⬆️ Promoted v8 from alpha to beta stability 🦭 Integrated the latest updates and fixes from Version 7
npm i react-hook-form@8.0.0-beta.0
🔬 feat: exp with react-compiler
🔬 feat: exp with react-compiler (#12309)
npm i react-hook-form@8.0.0-alpha.5
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Remove duplicate default value field
validateField skipping refs without setCustomValidity under native validation (#13782)validate function (#13777)useFieldArray touched fields handling (#13776)delayError timers for nested paths (#13775)getValues extracting unmarked field array entries (#13773)delayError timers remaining when a parent validates clean (#13771)delayError timers remaining after resetField() resets a parent (#13769)setValue() not revalidating dependencies when shouldValidate is enabled (#13765)reset() during handleSubmit() (#13761)reset() (#13762)register options at runtime remaining active (#13758)useController required validation not using the controlled value (#13756)useFieldArray marking the form dirty when the array default value is null (#13750)isValid not being recomputed when the errors prop is emptied (#13748)reset() during onChange (#13745)reset() during onChange (#13744)setValue() not triggering field array root validation when shouldValidate is enabled (#13743)getFieldState(name, formState) updates after reset() (#13741)@types/react peer dependency (#13781)@types/react peer dependencyvalidateField calling setCustomValidity on refs that don't implement it under native validationvalidate running a stale function instead of the latest onevalidate) being applied after reset in onChange and handleSubmitdelayError timers for nested paths not being cancelled when a parent validates clean or is reset via resetFieldgetValues(names, { dirtyFields }) returning every row of a field array instead of only the marked entriestouchedFields is not subscribeduseFieldArray emitting touchedFields in form state updates when unnecessaryuseFieldArray marking the form dirty when the array default is nullsetValue with shouldValidate not revalidating depssetValue with shouldValidate not triggering validation for a field array rootregister options at runtime still being applieduseController validating required against the input value instead of the controlled valueisValid not recomputing when the errors prop is emptiedgetFieldState(name, formState) with a resolver after reset()Add the Error Message component for displaying validation errors from React Hook Form.
resetField() (#13735)replace() (#13734)reset() during handleSubmit() (#13733)trigger() targets their parent (#13732)handleSubmit() (#13731)trigger() targets a field with only nested resolver errors (#13730)useWatch compute cache not being initialized with the initial output (#13728)FileList being lost during flatten/expand and omit undefined values in jsonToFormData (#13725)schemaErrorLookup reporting nested containers as exact-name errors (#13727)isValidating not being recomputed after unregister() clears validating fields (#13723)reset() (#13722)iterateFieldsByAction only breaking out of one loop level (#13718)setError() overwrites (#13716)reset() not clearing validating fields (#13714)remove() leaking deleted values onto surviving field-array items (#13713)validateField calling setCustomValidity once per key in validate-object mode (#13707)useFieldArray focus behavior for checkboxes and radio buttons in appended rows (#13705)getFieldState reading isValidating from the supplied form state (#13704)unregister() stripping kept values from the submit payload (#13703)useFormState not re-subscribing when control changes (#13702)reset() keeping dirtyFields out of sync with isDirty when keepValues is enabled (#13701)useForm not reconciling correctly when an Activity subtree is initially hidden (#13698)delayError timer fires (#13697)cloneObject throwing when checking Blob with instanceof (#13694)useFieldArray retaining stale fields after an Activity subtree reconnects (#13688)_isTracked to the remaining multi-key proxy/subscribe form-state checks (#13699)_isTracked helper for proxy/subscribe form-state checks (#13690)abortEarly parameter from iterateFieldsByAction (#13689)clearErrors (#13696)useWatch compute example variable names in JSDoc (#13700)joi from 18.2.1 to 18.2.5 (#13719)<ErrorMessage /> component for rendering a field's validation errorsetValue dirty state comparison ignoring valueAs* / setValueAs transformsdelayError timers not being cancelled when a parent is cleared or unregistereddelayError errors coming back once the pending timer firescriteriaMode not refreshing when form options are updated at runtimeresetField leaving stale validating state for the fieldreset not clearing isValidating and validatingFields, and ignoring keepIsValidatingreplace() leaving errors and touched state for removed rowsremove() leaking deleted values onto surviving itemshandleSubmit applying a stale resolver result after resethandleSubmit dropping resolver-reported root errors on submitresettrigger dropping registered nested errors when targeting their parenttrigger setting a parent error when the target has only nested resolver errorsuseWatch compute cache not being initialized with the initial outputflatten / jsonToFormData not preserving FileList and not skipping undefinedschemaErrorLookup reporting nested containers as exact-name errorsunregister not recomputing isValidating after clearing validatingFieldsunregister stripping a kept value from the submit payloaditerateFieldsByAction only breaking out of one loop levelsetError types from a previous validation surviving an overwritereact-server build not being publishedvalidateField calling setCustomValidity per key in validate-object modeuseFieldArray not focusing a checkbox or radio in the appended rowuseFieldArray not reconciling stale fields after an Activity subtree reconnectsuseForm never reconciling when an Activity subtree is hidden on its first rendergetFieldState not reading isValidating from the supplied form stateuseFormState not re-subscribing when control changesreset with keepValues leaving dirtyFields out of sync with isDirtycloneObject not guarding Blob before instanceofAdd shouldTouch option to trigger()
Add shouldTouch option to trigger() ([#13671](#13671))
setValue already supported shouldTouch; trigger() now does too. This allows you to mark field(s) as touched after manual or programmatic validation, such as validate-on-mount or multi-step form navigation, without needing a setValue workaround:
// Mark a single field as touched after validating it
await trigger('firstName', { shouldTouch: true });
// Mark every mounted field as touched after validating the whole form
await trigger(undefined, { shouldTouch: true });Add OpaqueTypes registry for opaque leaf types ([#13676](#13676))
Recursive type helpers (Path, DeepPartial, DeepRequired, FieldErrorsImpl, etc.) previously stopped only at Primitive | BrowserNativeObject. As a result, rich third-party value types such as Dayjs and Decimal could be recursively traversed member-by-member, potentially causing TypeScript to report "Excessive complexity" diagnostics on large form types.
You can now register a type as an opaque leaf via declaration merging:
import type { Dayjs } from 'dayjs';
declare module 'react-hook-form' {
interface OpaqueTypes {
dayjs: Dayjs;
}
}While the registry is empty, types remain identical to the existing behavior. This is purely opt-in.
useController().field.onChange()-like APIs. ([#13673](#13673))Controller under a null parent submitting undefined instead of a value. ([#13675](#13675))ERR_MODULE_NOT_FOUND when resolving react-hook-form due to a react-server declaration in Next.js builds. ([#13682](#13682))useWatch never reconciling when an Activity subtree is hidden on its first render. ([#13683](#13683))resetField not recomputing isValid for subscribe-only consumers. ([#13684](#13684))generateWatchOutput ignoring defaultValue for an array of names. ([#13686](#13686))useFormState never reconciling when an Activity subtree is hidden on its first render. ([#13687](#13687))Thanks to @giaBaoJS, @dfedoryshchev, @Alioth-91, @Masatoshi0227, @modosc, and @bluebill1049 for their contributions! 🎉
shouldTouch option for trigger()OpaqueTypes registry for opaque leaf typesuseController().field.onChange()-like APIsController under a null parent submitting undefined instead of a valueERR_MODULE_NOT_FOUND resolving react-hook-form due to a react-server declaration (Next.js builds)useWatch never reconciling when an Activity subtree is hidden on its first renderresetField not recomputing isValid for subscribe-only consumersgenerateWatchOutput ignoring defaultValue for an array of namesuseFormState never reconciling when an Activity subtree is hidden on its first renderAdd type-safe getErrors method. ( #13639 , #12853 )
useWatch returning stale values when the watched name changes to null. (#13668)unregister keepDirty behavior. (#13669)setValues for fields registered under object or array values. (#13667)Controller notifications for field array item roots. (#13665)setCustomValidity with criteriaMode: 'all'. (#13655)File, Blob, and FileList handling in flatten. (#13652)formState. (#13649)createFormControl performance. (#13648)cloneObject performance.unregister. (#13662)onChange-heavy workloads.any usage. (#13660)useFieldArray. (#13654)Thanks to @dfedoryshchev, @zigzagdev, @official-burak, @Jaybhade, @candymask0712, and @bluebill1049 for their contributions! 🎉
getErrors methodcreateFormControlunregister without subscribersflatten discarding File and Blob values instead of treating them as leaf nodesvalidateField not passing the field error to setCustomValidity when criteriaMode is allhasValidation treating falsy but valid values (min: 0, max: 0, minLength: 0, maxLength: 0, required: '') as no validationsetValue targeting a nested leaf not notifying a Controller registered on a field array item rootsetValues not updating fields registered under an object or array valueuseWatch returning a stale value on name change when the new value is nullunregister inverting keepDirty when broadcasting isDirtyfix min/max validation being skipped for valueAsDate fields
<Activity /> (#13633)valueAsDate fields (#13646)append, prepend, insert, and remove (#13645)useWatch preferring form defaultValues over the hook's own defaultValue (#13635)setValue emitting duplicate values state notifications (#13637)getFieldState error resolution for field paths (#13632)Thanks to @zigzagdev, @bluebill1049, @anshusaurav, @saulo-silva, @EduardF1, and @candymask0712 for their contributions! 🎉
<Activity />getFieldState error resolution from a field pathuseWatch discarding useForm({ defaultValues }) in favor of its own defaultValue before the form mountssetValue emitting a duplicate values state notification for fields without a native input refuseFieldArray root-level error (errors.name.root) being lost on append/prepend/insert/removemin/max validation being skipped for valueAsDate fieldsenhance <Form /> submit behavior
values + keepDirtyValues silently corrupting arrays and freezing objects on refetch (#13628)setValue not updating FieldArray values correctly (#13623)FormState type definitions (#13620)FieldArray and FormState exports (#13619)Blob coverage for FieldErrors type tests (#13624)ErrorNamespacePath type (#13615)<FormStateSubscribe /> back to <FormState /> (#13602)@babel/core from 7.26.0 to 7.29.7 (#13614)Thanks to @candymask0712, @EduardF1, @in-ch, @johnstrand, @wanxiankai, @nobu-da, and @bluebill1049 for their contributions! 🎉
handleSubmit returns the typed result of the onValid callback instead of discarding it<Form /> supports a function-based action (Server Action style) in addition to URL-string actionsreset() with resetOptions.keepDirtyValues freezing clean sibling fields in a nested object when another field in that object is dirtyFieldArray and FormState type exportssetValues not notifying useFieldArray subscribers, leaving rendered fields staleenhance getEventValue to handle file inputs
getEventValue to handle file inputs (#13289)clearErrors() (#13613)dirtyFields reference stability (#13612)useController): re-subscribe onChange/onBlur when control changes (#13603)undefined (#13287)Thanks to @candymask0712, @wanxiankai, @bluebill1049, @EduardF1, @johnstrand, and @in-ch for their contributions! 🎉
getEventValue to handle file inputsclearErrors()dirtyFields reference stabilityuseController not re-subscribing onChange/onBlur when control changesundefined🚀 feat: add opt-in delayError option for setValue
🚀 feat: add opt-in delayError option for setValue (#13337)
setValue("firstName", "Bill", {
delayError: 500, // delay validation error updates by 500ms
shouldValidate: true
});⚡ perf: improve getDirtyFields performance (#13590)
🐞 fix: expose resetDefaultValues through form context (#13598)
const { resetDefaultValues } = useFormContext();
resetDefaultValues();🐞 fix: setValue with shouldDirty now correctly updates dirty state when the form is disabled (#13594)
🐞 fix: preserve dirtyFields boolean values for registered array fields (#13586)
🐞 fix: restore FieldArray component export (#13596)
🧪 test: migrate test suite to Playwright and improve delayError test reliability (#13589)
thanks to @candymask0712, @wanxiankai, @a-y-ibrahim, @ankit1324 & @abnud11
delayError option for setValue to delay validation error updatesresetDefaultValues through form contextsetValue with shouldDirty not updating dirty state correctly when the form is disableddirtyFields boolean values for registered array fieldsFieldArray component exportgetDirtyFields performance, especially for larger forms and deeply nested values< FieldArray control = { control } name = "test" render = { ( { fields } ) => null } />
⚜️ feat: FieldArray component (#13394)
<FieldArray
control={control}
name="test"
render={({ fields }) => null}
/>🐞 fix #13538 useFieldArray min 1 item validation error changes its location in the errors object (#13539)
🐞 fix #13569 calling reset triggers subscribe with latest name instead of undefined (#13574)
🐞 fix(useController): reflect cleared parent object in controlled fields (#13550) (#13553)
🐞 fix(flatten): preserve Date values as leaf nodes (#13566)
🐛 fix(unset): guard against prototype keyword path traversal (#13559) (#13560)
🏸 improve setValue api with shrink value (#13576)
👝 close #13577 improve re-render with useFieldArray reset (#13578)
🐞 fix #13575 issue: clearErrors changes the name value from form.subscribe (#13579)
thanks to @DakshSinghDhami, @JSap0914, @tran-simon & @EduardF1
FieldArray component built on useFieldArray for controlled field-array renderingclearErrors changing the name value from form.subscribereset triggering subscribe with the latest name instead of undefinedsetValues leaving stale elements behind when shrinking a field arrayflatten preserving Date values as leaf nodesuseFieldArray min length validation error changing location in the errors objectuseController not reflecting a cleared parent object in controlled fieldsunset() against prototype-path traversal (__proto__ / constructor / prototype), matching the existing set() guarduseFieldArray reset🧄 feat: disable useFieldArray fields
disabled prop support for individual fields within useFieldArraydeepEqual incorrectly treating empty array [] and empty plain object {} as equalonChange, setValid, dirty checking, setValue, and setValues performance🚷 feat: use field array disabled
🚷 feat: use field array disabled (#13520)
useFieldArray({ disabled, name: 'test' })🐞 fix controller onChange promise (#13518)
🐞 fix: track visited pairs in deepEqual to avoid false positives with shared object references (#13515)
🐞 fix #12651 issue: field validation with shouldUseNativeValidation does not behave native like for radio groups (#13512)
🐞 fix #12754 createFormControl breaks with fast refresh in dev mode (#13511)
🐞 close #12709 #12750 StrictMode would remove field value & get mounted again (#13508)
🐞 fix #13505 issue: In v8 with React compiler, a change in formState errors does not cause child components to re-render (#13510)
thanks to @DucMinhNe & @louzhedong
disabled option to useFieldArrayController onChange promise return typedeepEqual false positives with shared object referencesshouldUseNativeValidation behavior for radio groupscreateFormControl stability with fast refresh in dev modeStrictMode value preservation during remountformState.errors reactivity with React compiler🦷 update type dirtyFields typing for field arrays with undefined entries ( #13492 ) 🐞 fix: recover Controller fields after reset without rerender (RN
🦷 update type dirtyFields typing for field arrays with undefined entries (#13492)
🐞 fix: recover Controller fields after reset without rerender (RN issue #13455) (#13497)
🐞 fix useFormState().isDirty race with async resolver in onChange mode (#13495)
🐞 fix: use reactive values prop over defaultValues when shouldUnregister is true (#13485)
🐞 fix deepEqual for empty non-plain objects (#13493)
thanks to @cyphercodes
Controller fields after reset without rerender (RN issue #13455)useFormState().isDirty race with async resolver in onChange modevalues prop over defaultValues when shouldUnregister is truedeepEqual for empty non-plain objectsdirtyFields typing for field arrays with undefined entries🥡 feat: add resetDefaultValues API
🥡 feat: add resetDefaultValues API (#13427)
https://react-hook-form.com/docs/useform/resetdefaultvalues
const { resetDefaultValues } = useForm();
resetDefaultValues(currentValues);
🐚 harden get() against prototype-path traversal (proto / constructor / prototype) (#13479) 🐞 fix FieldArray errors overriding nested fields (#13476) 🐞 fix inconsistent reset({}) behavior requiring double-call to take effect (#13473) 🐞 fix: preserve values with shouldUnregister (#13464) 🐞 fix stale isDirty in subscribe payload after reset(..., { keepValues: true }) (#13461) 👝 save bundle size (#13468)
thanks to @puneetdixit200 & @dfedoryshchev
resetDefaultValues APIisDirty in subscribe payload after reset(..., { keepValues: true })shouldUnregisterreset({}) behavior requiring double-call to take effectFieldArray errors overriding nested fieldsget() against prototype-path traversal (__proto__ / constructor / prototype)🐞 fix: pass options parameter through setValues to enable validation (#13457) 🐞 fix(setValues): emit whole-form change without stale name/type (#13450
🐞 fix: pass options parameter through setValues to enable validation (#13457) 🐞 fix(setValues): emit whole-form change without stale name/type (#13450) 🚗 perf(setValues): thread skipClone through setFieldValue (#13448) 🚗 perf(setValues): skip redundant per-field deep clones (#13445) Revert "🐞 fix: treat NaN as empty when valueAsNumber is true in validateField (#13388)"
thanks to @philibea & @maxkostow
setValue updatesNaN as empty when valueAsNumber is true in validateFieldsetValues pass options parameter through to enable validationsetValues emit whole-form change without stale name/typesetValues skip redundant per-field deep clonessetValues thread skipClone through setFieldValue🪭 close #13141 improve isDirty sync with dirtyFields state (#13370) 🐞 fix isValidating reactivity when validatingFields is not subscribed (#13440) 🛺 t
🪭 close #13141 improve isDirty sync with dirtyFields state (#13370) 🐞 fix isValidating reactivity when validatingFields is not subscribed (#13440) 🛺 test: fix duplicate-word typos in test descriptions (#13439) 🐞 fix #13436: errors state when using form level validation (#13437) 🐞 fix #13429 append({ obj: null }) is silently replaced by defaultValues after remove() (#13435) 🐞 fix native validation tooltip suppression caused by duplicate submit-error focus (#13432) 🐞 fix: propagate setValues updates to mounted Controller fields (#13431) 🐞 fix: rreserve reset values for conditionally mounted Controller fields with shouldUnregister 🐞 fix: useFieldArray remove leaves array with empty object when using values prop (#13422) 🐞 fix #13260: notify all matching field-array roots on nested setValue updates (#13420) 🐞 fix #13104: preserve nested resolver field-array errors in trigger() (#13419) 🐞 fix #13413: preserve formState.defaultValues when useFieldArray + watch are used together 📝 docs: fix JSDoc for IsNever, register, and getFieldState (#13410) (#13411) 🐞 fix(Watch): restore TypeScript 4 compatibility (#13409)
Big thanks to @dfedoryshchev for multiple fixes, and to @EduardF1, @in-ch and @johnstrand.
isDirty sync with dirtyFields stateformState.defaultValues when useFieldArray and watch are used togethertrigger()setValue updatesuseFieldArray remove leaves array with empty object when using values propController fields with shouldUnregistersetValues updates to mounted Controller fieldsappend({ obj: null }) silently replaced by defaultValues after remove()isValidating reactivity when validatingFields is not subscribed🦧 feat: improve get dirty fields prune empty fields
🦧 feat: improve get dirty fields prune empty fields (#13363)
+ dirtyFields: { test: [{ data: false }] }
- dirtyFields: {} // removed the empty node with false value
🎹 typescript 6.0 (#13330) 🌡️ chore: minor improvement on setValue & reset (#13366) 🐞 fix #13403: include setValues in FormProvider context value (#13404) 🐞 fix: recompute isDirty after re-registering a previously unregistered field (#13399) 🐞 fix: preserve watch updates on field array unmount fixes #13375 (#13385) 🐞 fix: prevent useWatch re-render when unrelated field validation is … (#13398)
thanks to @dfedoryshchev, @cyky & @gkarabelos
getDirtyFields to prune empty fieldssetValues in FormProvider context valueuseWatch re-render when unrelated field validation occursisDirty after re-registering a previously unregistered field```tsx setValues((data) => { return { ...data, name: 'test' } })
🪇 feat: setValues (#13201)
setValues((data) => {
return {
...data,
name: 'test'
}
})
setValues(formValues);
🐞 fix: preserve previous field value when useController name changes (#13395) 🐞 fix: handle null parent when unregistering nested field (#13396) 🐞 fix: treat NaN as empty when valueAsNumber is true in validateField (#13388) 🪢 fix build to exclude test files (#13387)
thanks to @Yihao-G & @mixelburg
setValues APIuseController name changesNaN as empty when valueAsNumber is true in validateField⚡perf: memoize submit (#13378) 🚉 perf: improve deepEqual performance (#13362) 👀 perf: skip re-render in setValue when value is unchanged (#13352) ✂️ r
⚡perf: memoize submit (#13378) 🚉 perf: improve deepEqual performance (#13362) 👀 perf: skip re-render in setValue when value is unchanged (#13352) ✂️ remove unneeded flag check for shouldDirty 🚨 fix: safely access field._f during register (#13365) 🧹 close #13298: improve fieldState errors when resolver uses dot-notation string keys (#13350) 🐞 fix #13178: update state correctly in watch callback with Controller, trigger, and reset (#13180) 🐞 fix #13331: skip field array validation when mode is onBlur (#13333) 🐞 fix #13334 sDirty remains false after deletion an item with shouldDirty: true (#13357) 🐞 fix: handle nested field when parent defaultValue is null (#13348)
thanks to @Prasadzoman, @cyphercodes, @lorenzoceglia, @rizwan-rizu, @tomeelog & @ap0nia
setValues that was accidentally included in patch; fix build to exclude test files🐞 fix: add isDirty check for numeric string keys in defaultValues (issue #13346) (#13347) 🐞 fix: prevent setValue with shouldDirty from polluting unre
🐞 fix: add isDirty check for numeric string keys in defaultValues (issue #13346) (#13347) 🐞 fix: prevent setValue with shouldDirty from polluting unrelated dirty fields (#13326) 🐞 fix: memoize control in HookFormControlContext to prevent render conflicts (#13272) (#13312) 🐞 fix: isNameInFieldArray should check all ancestor paths for nested field arrays (#13318) 🐞 fix: #13320 formState.isValid incorrect on Controller re-mount (#13324)
thanks to @6810779s, @candymask0712, @olagokemills, @shahmir-oscilar & @bae080311
setValue with shouldDirty from polluting unrelated dirty fieldsHookFormControlContext to prevent render conflictsisNameInFieldArray should check all ancestor paths for nested field arraysformState.isValid incorrect on Controller re-mount⚓️ feat: built-in form level validate
⚓️ feat: built-in form level validate (#13195)
useForm({
validate: async ({ formValues }: FormValidateResult) => {
if (formValues.test1.length > formValues.test.length) {
return {
type: 'formError',
message: 'something is wrong here',
};
}
if (formValues.test === 'test') {
return 'direct error message';
}
return true;
},
});
🐞 fix: prevent useFieldArray from marking unrelated fields as dirty (#13299) 🐞 fix #13300 checkbox form validation ignored with native validation (#13310) 🌉 allow subscribe formState to track submit state (#13319)
thanks to @WiXSL, @BrendanC23 & @6810779s
validate optionformState to track submit stateuseFieldArray from marking unrelated fields as dirty🕵️♂️ fix: use DeepPartialSkipArrayKey for WatchObserver value parameter (#13278) 🧹 fix(clearErrors): emit name signal for targeted field updates
🕵️♂️ fix: use DeepPartialSkipArrayKey for WatchObserver value parameter (#13278) 🧹 fix(clearErrors): emit name signal for targeted field updates (#13280)
thanks to @veeceey, @kaigritun, @pgoslatara & @seongbiny
clearErrors emit name signal for targeted field updatesDeepPartialSkipArrayKey for WatchObserver value parameter🐞 fix #13250 issue with booleans_as_integers
🐞 fix #13250 issue with booleans_as_integers (#13252)
⚡ perf: memoize FormProvider context value to prevent unnecessary rerenders (#13235) 🚄 perf: separate control context to prevent unnecessary rerenders
⚡ perf: memoize FormProvider context value to prevent unnecessary rerenders (#13235) 🚄 perf: separate control context to prevent unnecessary rerenders (#13234) 🐞 fix: update isValid when field disabled state changes (#13231) 👌 chore: optimize bundle size via safe terser options (#13243) (#13244)
thanks to @kamja44, @a28689604 & @newsiberian
FormProvider context value to prevent unnecessary rerendersisValid when field disabled state changes✅ watch type improvement (#13228) 🐞 fix: prevent field array ghost elements with keepDirtyValues (#13188) 🐞 fix: improve invalid date handling in deep
✅ watch type improvement (#13228) 🐞 fix: prevent field array ghost elements with keepDirtyValues (#13188) 🐞 fix: improve invalid date handling in deepEqual and validation (#13230) 🐞 fix(types): handle branded types correctly in DeepPartial (#13222) 🐞 fix native validation focus issue (#13220) 🐞 change spread operator to set name with depricated names prop, then override with new name prop is supplied (#13214) 🐞 fix: prevent duplicate subscription trigger in setValue (#13206) (#13209) 👌 chore: fix lib type check include tests (#13229)
thanks to @EdwardEB, @constantly-dev & @a28689604
keepDirtyValueswatch return typesdeepEqual and validationDeepPartialsetValue…function methods on elements (#13190) 🪖 chore: cve-2025-67779 (#13196) 🪖 chore: cve-2025-55184 & cve-2025-55183 (#13194) 🪖 chore: CVE-2025-55182 Criti…
📏 feat: align <Watch /> API with useWatch (#13192) 🤦🏻♂️ chore: update @deprecated names prop on <Watch /> (#13198) 🏥 chore: safely call function methods on elements (#13190) 🪖 chore: cve-2025-67779 (#13196) 🪖 chore: cve-2025-55184 & cve-2025-55183 (#13194) 🪖 chore: CVE-2025-55182 Critical RCE vulnerabilty (#13175) 🔬 test: add regression tests for #12837 and #13136 (#13187) 🐞 fix(reset): preserve isValid state when keepIsValid option is used (#13173) 🐞 fix: ensure each createFormControl.subscribe subscription listens only to the changes it subscribes to (#12968) 🐞 fix(validation): batch isValidating state updates with validation result (#13181) 🐞 fix(createFormControl): resolve race condition between setError and setFocus (#13138) (#13169) 🧿 fix <Watch /> control prop type (#13189) 🔔 chore: clean cloneObject logic (#13179)
thanks to @PierreCrb, @a28689604, @AnuragM7666, @ap0nia, @dusan233 & @hlongc
<Watch /> API with useWatchisValid state when keepIsValid option is usedcreateFormControl.subscribe subscription listens only to subscribed changesisValidating state updates with validation resultsetError and setFocus```tsx import { useForm, FormStateSubscribe } from 'react-hook-form';
🎧 feat: <FormStateSubscribe /> component (#13142)
import { useForm, FormStateSubscribe } from 'react-hook-form';
const App = () => {
const { register, control } = useForm();
return (
<div>
<form>
<input {...register('foo')} />
<input {...register('bar')} />
</form>
{/* re-render only when formState of `foo` changes */}
<FormStateSubscribe
control={control}
name={"foo"}
render={({errors}) => <span>{errors.foo?.message}</span>}
/>
</div>
);
};
🐞 fix: clear validation errors synchronously in reset() to fix Next.js 16 Server Actions issue (#13139) Revert "✨ fix(types): allow undefined value with async defaultValues in Contr…" (#13171)
thanks to @xiangnuans, @abnud11, @ntatoud & @ap0nia
<FormStateSubscribe /> componentreset() to fix Next.js 16 Server Actions issue🎯 feat: add exact to useController props
🎯 feat: add exact to useController props (#13154)
useForm({
defaultValues: {
user: {
name: ''
}
}
})
<Controller control={control} name="user" exact={false} /> // subscribe to all user object
✨ fix(types): allow undefined value with async defaultValues in Controller (#13160) 🐞 fix(types): correct PathValueImpl type inference (#13150)
thanks to @ap0nia, @Fasping & @joseph0926
exact option for useController propsundefined value with async defaultValues in ControllerPathValueImpl type inference⚡ perf: reduce redundant property access in getDirtyFields (#13146) 🐞 fix(createFormControl): skip setValid() during batch array updates (#13140) 🐞 fi
⚡ perf: reduce redundant property access in getDirtyFields (#13146) 🐞 fix(createFormControl): skip setValid() during batch array updates (#13140) 🐞 fix(useForm): recompute isValid after reset when values update asynchronously (#13126) 🐞 fix(deepEqual): handle NaN comparison correctly using Object.is (#13120)
thanks to @kimtaejin3, @a28689604 & @WuMingDao
getDirtyFieldssetValid() during batch array updatesisValid after reset when values update asynchronouslyObject.is in deepEqual🎥 feat: make useWatch and useController to react to name change (#13070) 🐛 fix: watch() returning undefined immediately after reset() - Issue #13088 (
🎥 feat: make useWatch and useController to react to name change (#13070)
🐛 fix: watch() returning undefined immediately after reset() - Issue #13088 (#13091)
🐞 fix <Watch />: correct render function parameter typing (#13108)
thanks to @aspirisen, @scato3, @dusan233 & @zoldyzdk
useWatch and useController now react to name prop changeswatch() returning undefined immediately after reset()<Watch /> component```tsx import { useForm, Watch } from 'react-hook-form';
🧿 feat: <Watch /> component (#12986)
import { useForm, Watch } from 'react-hook-form';
const App = () => {
const { register, control } = useForm();
return (
<div>
<form>
<input {...register('foo')} />
<input {...register('bar')} />
</form>
{/* re-render only when value of `foo` changes */}
<Watch
control={control}
names={['foo']}
render={([foo]) => <span>{foo}</span>}
/>
</div>
);
};
🐞 fix: respect parent-provided useFieldArray rules (#13082) (#13083
🐞 fix: getDirtyFields submit fields with null values when using useForm (#13079)
thanks to @tesseractjh, @Han5991 & @jonathanarnault
<Watch /> componentuseFieldArray rulesgetDirtyFields submit fields with null values when using useForm🚏 Support optional array fields in PathValueImpl type (#13057) 🐞 fix: preserve Controller's defaultValue with shouldUnregister prop (#13063) ✂ chore:
🚏 Support optional array fields in PathValueImpl type (#13057)
🐞 fix: preserve Controller's defaultValue with shouldUnregister prop (#13063)
✂ chore: remove unused field ids ref in useFieldArray (#13066)
thanks to @MPrieur-chaps, @gynekolog & @uk960214
PathValueImpl typeController's defaultValue with shouldUnregister prop🥢 feat: extract form values by form state
🥢 feat: extract form values by form state (#12936)
getValues(undefined, { dirtyFields: true }); // return only dirty fields
getValues(undefined, { touchedFields: true }); // return only touched fields
🦍 feat: improve get dirty fields logic (#13049) 🐿️ chore: remove duplicated function isMessage (#13050) 🐞 fix: use field name to update isValidating fields (#13000) 🐞 fix: unregister previous field when switching conditional Controllers (#13041) 🐞 fix: only excuse trigger function when deps has a valid array (#13056)
thanks to @candymask0712, @GorkemKir, @kimtaejin3, @m2na7 & @abnud11
formStateisValidating fields👨🔧 prevent onBlur for readOnly fields (#12971) 🐞 fix #12988 sync two defaultValues after reset with new defaultValues (#12990) 🐞 fix: do not override
👨🔧 prevent onBlur for readOnly fields (#12971) 🐞 fix #12988 sync two defaultValues after reset with new defaultValues (#12990) 🐞 fix: do not override prototype of data in cloneObject (#12985) 🐞 fix field name type conflict in nested FieldErrors (#12972)
thanks to @candymask0712, @Adityapradh, @Ty3uK & @kichikawa57
defaultValues after reset with new defaultValuesFieldErrorsonBlur for readOnly fieldsdata in cloneObjectRevert "⌨️ fix: watch return type based on defaultValue (#12896)"
Revert "⌨️ fix: watch return type based on defaultValue (#12896)"
watch return type change based on defaultValue (caused regressions)🧮 feat: compute prop for useWatch subscription
🧮 feat: compute prop for useWatch subscription (#12503)
type FormValue = {
test: string;
}
const watchedValue = useWatch({
control: methods.control,
compute: (data: FormValue) => {
if (data.test?.length) {
return data.test;
}
return '';
},
});
type FormValue = {
test: string;
}
const watchedValue = useWatch({
control: methods.control,
name: 'test',
compute: (data: string) => {
return data.length > 3 ? data : '';
},
});
👨🔧 trigger watch callbacks in response to value changes only (#12945) 🙏 track name with setValue subscription callbacks (#12946) ⌨️ fix: watch return type based on defaultValue (#12896) 🐞 fix #12959 subscribe with latest defaultValues #12961 🐞 fix: handle explicit "multipart/form-data" encType in Form Component (#12948) 🐞 fix(build): Remove React wildcard import to resolve ESM build issues (#12942) 🦭 chore: improve exclude patterns (#12935) 🐿️ chore: remove unused omit function (#12958)
Big thanks to @joshkel for helping with some of the subscription bugs! and also @kamja44, @mrazauskas, @codepunkt, @afontcu and @rururux
compute prop for useWatch subscriptiondefaultValuessetValue subscription callbacks"multipart/form-data" encType in <Form /> component🦘 feat: reset new keepFieldsRef options keep fields reference
🦘 feat: reset new keepFieldsRef options keep fields reference (#12923)
// This option will skip input reference gets reset and avoid re-register input reference after reset
reset(data, { keepFieldsRef: true })
reset with keepFieldsRef option to keep fields referenceSet from clone object🦘 feat: reset new keepFieldsRef options keep fields reference
🦘 feat: reset new keepFieldsRef options keep fields reference (#12923)
// This option will skip input reference gets reset and avoid re-register input reference after reset
reset(data, { keepFieldsRef: true })
🪱 feat: support deep equality checking with circular references (#12914) 🐞 fix #12900 issue with formData reference clone (#12906) 🐞 fix #12873 issue
🪱 feat: support deep equality checking with circular references (#12914) 🐞 fix #12900 issue with formData reference clone (#12906) 🐞 fix #12873 issue with undefined value for submit data (#12905) 🐞 fix case when useWatch accept object variable param (#12897) 🐞 fix: typo in UseFormSubscribe and missing event type for callback data in subscribe (#12904) Revert "⌨️ rename to UseFormResetFieldOptions for type consistency" (#12907)
thanks to @aspirisen @n8pjl @SKOLZ @pushys & @candymask0712
formData reference cloneundefined value for submit datauseWatch object variable param handling🔧 check window.crypto undefined
🔧 check window.crypto undefined (#12893)
⌨️ feat: add FieldArrayPathByValue type (#12481) 📇 feat #12813 use stringToPath to prevent error at field name with quotes (#12858) 🧧 default to crypt
⌨️ feat: add FieldArrayPathByValue type (#12481) 📇 feat #12813 use stringToPath to prevent error at field name with quotes (#12858) 🧧 default to crypto.randomUUID in generateId (#12890) 🐞 close #12857 incorrect formControl return from useForm (#12878) 🐞 fix Initial useFieldArray fields (#12847) 🥷 chore: improve type import (#12879) 📇 chore: Added displayName to useFormContext (#11448) ✍️ chore: fix typo in assert-esm-exports.mjs (#12860)
thanks to @mastermatt, @clonemycode, @dusan233, @candymask0712, @tran-simon & @adnanalbeda
FieldArrayPathByValue typestringToPath to prevent errors at field names containing quotesformControl return from useFormuseFieldArray fields population🫚 feat: root errors count in schema error lookup (#12839) 👁️ feat: focus form field for errors supplied by errors prop (#12805) ⌨️ feat: add and expor
🫚 feat: root errors count in schema error lookup (#12839) 👁️ feat: focus form field for errors supplied by errors prop (#12805) ⌨️ feat: add and export options config for resetField api (#12819) 🐞 close #12707 useController focus function runtime issue (#12843) 🐞 fix: add proper types to form.subscribe (#12850) 🐞 fix: add type info for callback args in subscribe (#12859) 🔄 close #12835 revert original fix on errors empty object check (#12846)
thanks to @candymask0712, @CertainlyAria, @jkbach, @chrisgarber and @evgeniyworkbel
errors propUseFormResetFieldOptions type for resetField APIsubscribeform.subscribeuseController focus function runtime issuesetValue skips values not in own properties (prevents infinite call stack with self-referencing proto)useFieldArraymode and reValidateMode reactivityvalues prop regressionsubscribe to the entire form but only return updated value with certain condition
useWatch new compute propstype FormValue = {
test: string;
}
const watchedValue = useWatch({
control: methods.control,
compute: (data: FormValue) => {
if (data.test?.length) {
return data.test;
}
return '';
},
});
type FormValue = {
test: string;
}
const watchedValue = useWatch({
control: methods.control,
name: 'test',
compute: (data: string) => {
return data.length > 3 ? data : '';
},
});
🐞 fix: Changes setValue to skip values that are not in ownProperties to prevent infinite call stack (#12731) 🐞 fix: checkbox duplication handling in u
🐞 fix: Changes setValue to skip values that are not in ownProperties to prevent infinite call stack (#12731) 🐞 fix: checkbox duplication handling in useFieldArray (#12793) 🐞 fix: make mode and reValidateMode reactive (#12803)
Revert "📭 close #12773 pass input ref instead partial (#12775)"
Revert "📭 close #12773 pass input ref instead partial (#12775)"
🐞 fix #12785 regression on default value gets overwritten by values props (#12790) 🐞 fix: use layoutEffect in useWatch for subscription similar to use
🐞 fix #12785 regression on default value gets overwritten by values props (#12790) 🐞 fix: use layoutEffect in useWatch for subscription similar to useForm (#12786) 🐞 fix #12772 reset form useWatch to utilize ref for defaultValue and … (#12780) 📭 close #12773 pass input ref instead partial (#12775)
🐞 fix #12761 #12762 issue with usage reset isReady formState (#12765) 🐞 fix #12763 import warning with named exports
🐞 fix #12761 #12762 issue with usage reset isReady formState (#12765) 🐞 fix #12763 import warning with named exports (#12764)
⏰ feat: introduce isReady state for subscription
⏰ feat: introduce isReady state for subscription (#12568)
const { formState: { isReady }, setValue } = useForm()
useEffect(() => {
// form subscription setup is ready
if (isReady) setValue('test', 'value')
}, [isReady])
🌗 feat: support reactive mode and reValidateMode (#12743) 🐞 fix #12741 regression on move/swap on useFieldArray input update (#12749) 🐞 fix: use useIsomorphicLayoutEffect to address warning in SSR (#12738)
mode and reValidateMode supportisReady state for subscriptionuseFieldArray input updateuseIsomorphicLayoutEffect to address SSR warning⏰ feat: introduce isReady state for subscription
⏰ feat: introduce isReady state for subscription (#12568)
const { formState: { isReady }, setValue } = useForm()
useEffect(() => {
// form subscription setup is ready
if (isReady) setValue('test', 'value')
}, [isReady])
🌗 feat: support reactive mode and reValidateMode (#12743) 🐞 fix #12741 regression on move/swap on useFieldArray input update (#12749) 🐞 fix: use useIsomorphicLayoutEffect to address warning in SSR (#12738)
Allow us to start subscribing outside of the React component
⚡️ createFormControl
const { formControl, control } = createFormControl(props)
function App() {
const { register } = useForm({
formControl,
})
return <form />
}
function Test() {
useFormState({
control // no longer need context api
})
}
⚡️ subscribe
const { formControl } = createFormControl(props)
formControl.subscribe({
formState: { isDirty: true },
callback: (formState) => {
if (formState.isDirty) {
// do something here
}
}
})
function App() {
const { register } = useForm({
formControl,
})
return <form />
}
🪲 fix https://github.com/react-hook-form/react-hook-form/issues/12680: Update Fieldarray Unmount Status (https://github.com/react-hook-form/react-hook-form/pull/12690) 🫡 fix: improve type inference for useFormContext (https://github.com/react-hook-form/react-hook-form/pull/12689) 👮♂️ feat: infer resolver output types (https://github.com/react-hook-form/react-hook-form/pull/12638) 🐞 fix(useForm): make values and defaultValues work correctly with createFormControl and useMemo (https://github.com/react-hook-form/react-hook-form/pull/12675) 🥹 close https://github.com/react-hook-form/react-hook-form/issues/12665 issue with values not populate form 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12665 regression on values over take default values 🫡 allow use of handleSubmit with native events (https://github.com/react-hook-form/react-hook-form/pull/12645) 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12631 revalidateMode issue with useFieldArray validation (https://github.com/react-hook-form/react-hook-form/pull/12646) 🦥 close https://github.com/react-hook-form/react-hook-form/issues/12634 allow components with useController hook be memoized (https://github.com/react-hook-form/react-hook-form/pull/12635) 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12580 setError in useEffect does not work when used inside the FormProvider context (https://github.com/react-hook-form/react-hook-form/pull/12642) 🛟 fix: add the condition to prevent infinite render with errors prop (https://github.com/react-hook-form/react-hook-form/pull/12622) 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12580 valid state update with onBlur mode (https://github.com/react-hook-form/react-hook-form/pull/12612) 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12572 disabled field value not get undefined in resolver (https://github.com/react-hook-form/react-hook-form/pull/12573) 🦾 feat: reference stable for useWatch defaultValue (https://github.com/react-hook-form/react-hook-form/pull/12564) ⏰ chore: remove typetest folder from build (https://github.com/react-hook-form/react-hook-form/pull/12555) 🐞 fix https://github.com/react-hook-form/react-hook-form/issues/12532 useController should unregister issue with strict mode (https://github.com/react-hook-form/react-hook-form/pull/12538) 👁️ feat: improve reference update with useWatch (https://github.com/react-hook-form/react-hook-form/pull/12537) 🦥 close https://github.com/react-hook-form/react-hook-form/issues/12531 disabled state issue with touched and dirty (https://github.com/react-hook-form/react-hook-form/pull/12536)
thanks to @jtomaszewski, @joshkel, @candymask0712, @kotarella1110, @jorisre, @jtomaszewski, @controversial and @jedahu
createFormControl and subscribe functionuseController hook to be memoizeduseWatch defaultValuehandleSubmit with native events supportuseFormContextvalues and defaultValues work correctly with createFormControl and useMemoonBlur modedisabled field value not returned as undefined in resolverrevalidateMode issue with useFieldArray validationuseController unregister issue with strict modesetError in useEffect not working inside FormProvider contextuseController return props reference stabilityYour coding agent can read these notes before it upgrades. Set up the MCP server →