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
🍨 close #6193 to remove shallow merge with defaultValue with watch (#6201) 💨 improve useFieldArray perf
🍨 close #6193 to remove shallow merge with defaultValue with watch (#6201)
💨 improve useFieldArray perf (#6215)
🐞 fix #6186 delayError with set error after delay
🐞 fix #6186 delayError with set error after delay (#6188)
One column per quarter.
🖨️ include reset clone value for array/object/date (#6183) 👨🏻🔧 fix rendering order with useFieldArray (#6182) 🎭 skip the shallow clone with useForm r
🖨️ include reset clone value for array/object/date (#6183) 👨🏻🔧 fix rendering order with useFieldArray (#6182) 🎭 skip the shallow clone with useForm return (#6154) ⌨️ fix: WatchObserver info name type (#6171)
This new release is going to be quite an improvement over the existing version. We will start with next (beta) version.
This new release is going to be quite an improvement over the existing version. We will start with next (beta) version.
🏎 Performance enhancement at scale
React hook from is getting a general performance upgrade and enhancement. The following API will get big performance improvement at scale, which means it may not improve on small or medium-size applications, but rather large and complex applications with subscriptions.
useWatchuseFieldArraywatchgetValuesResult Env: dev build + throttle 4x CPU + Chrome + MAC M1
| Version | Flat (1000) | Nested (1000) | Deeply Nested (1000 * 2 * 2000) | Flat (1000) Mount |
|---|---|---|---|---|
7.13.0-next.0 |
18ms | 20ms | 200ms | 470ms (no change) |
7.11.1 |
40ms | 43ms | 2000ms | 470ms |
📦 Package size-reduction
Total Size: 30.3 kB
| Filename | Size | Change | |
|---|---|---|---|
dist/index.cjs.js |
8.39 kB | -445 B (5%) | ✅ |
dist/index.esm.js |
13.4 kB | -656 B (4%) | |
dist/index.umd.js |
8.52 kB | -420 B (4%) |
🔫 Trigger
useFieldArray({ name: 'test' })
trigger('name') // will trigger the whole field array to validate
📑 register
disabled prop as an option to toggle input disable attributeref callbackregister('test', { disabled: true }) // will set value to undeinfed and pass disabled down to the input attribute
<div {...register('test')}>
<input name="test" /> // this input will be registered
</div>
👀 useWatch
disabled prop to toggle the state subscription.useWatch({ disabled: true }) // you toggle the subscription
⌨️ useFormState
disabled prop to toggle the state subscription.useFormState({ disabled: true }) // you toggle the subscription
⛳️ setValue
<input {...register('test.0.firstName')} />
setValue('test', [{ firstName: 'bill' }, {firstName: 'kotaro}, {firstName: 'joris'}]) // this will works
✌🏻 close #6124 move isValid state into a separate function to unblock validation 🐞 fix #6105 deep equal issue with undefined node
✌🏻 close #6124 move isValid state into a separate function to unblock validation 🐞 fix #6105 deep equal issue with undefined node (#6107)
🐞 fix #6090 issue with set defaultValue as string with valueAs (#6093) 🐞 make sure the field is set before setting it to mounted (#6084) 🐞 fix #6081 i
🐞 fix #6090 issue with set defaultValue as string with valueAs (#6093)
🐞 make sure the field is set before setting it to mounted (#6084)
🐞 fix #6081 issue with delayError with formState (#6083)
Nothing published for this version
⌚ UX: useForm config delayError (#5935) `tsx useForm({ delayError: 500 // delay error appear with 500ms }) `
⌚ UX: useForm config delayError (#5935)
useForm({
delayError: 500 // delay error appear with 500ms
})
⌨️ stricter type check on onChange event type (#6030) 🐞 fix #6035 issue with Controller select toggle inValid state (#6069) 🐞 fix #6055 missing dirtyFields subscribe in useFieldArray actions (#6059) 🐞 fix #6052 miss marking controller as mounted (#6053)
useForm config delayErroruseForm({
delayError: 500, // delay error appear with 500ms
});
Nothing published for this version
🐛 fix #5944 reset issue with unmounted useFieldArray (#5954) 🐛 fix #5942 case when value is supplied as null/undefined ⌨️ close #5928 improve useWatch
🐛 fix #5944 reset issue with unmounted useFieldArray (#5954) 🐛 fix #5942 case when value is supplied as null/undefined ⌨️ close #5928 improve useWatch and watch types (#5945) 🎮 fix #5936 regression with controller value transform (#5937)
🚅 update method for useFieldArray
🚅 update method for useFieldArray (#5835)
const { update } = useFieldArray();
update(0, data); // update an individual field array node
🔄 fix #5911 issue with reset clear flag for Controller (#5912) ⌨️ fix #5839 issue with null type with deepMap (#5896) 🚌 close #5857 setValue missing await with trigger validation (#5859) 👨🏻💻 chore: allow readonly tuple in useWatch generic params (#5845)
update method to update an field array inputsconst { update } = useFieldArray();
update(0, data); // update an individual field array node
Nothing published for this version
🗝️ fix #5808 DeepMap extend issue with File (#5813) 🕵️ fix #5816 variable name typo in useController
🗝️ fix #5808 DeepMap extend issue with File (#5813) 🕵️ fix #5816 variable name typo in useController
Nothing published for this version
🍌 deprecated defaultValue at useFieldArray as a required prop
🍌 deprecated defaultValue at useFieldArray as a required prop (#5636)
function Test() {
const { control, register } = useForm();
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
control,
name: "test",
});
return (
{fields.map((field, index) => (
<input
key={field.id} // important to include key with field's id
{...register(`test.${index}.value`)}
- defaultValue={field.value} // no longer required
/>
))}
);
}
👨🏻💻 fix #5787 controller type for defaultValue (#5792) 😿 fix #5777 regression on unmount defaultValue with shouldUnregister:true (#5778) 📅 close #5724 setValue skip parse date time as object (#5729) 🎮 #5712 enable controller with native validation (#5716) 🐛 fix spread non-primitive values in nested array fields (#5705) 🎪 batch single watch update (#5707) 🏟️ batch unregister inputs (#5706) 🐞 fix #5699 trigger not focusing first input with error (#5700)
defaultValue is no longer a required prop for register input with useFieldArray🥇 feature: support browser native form control
🥇 feature: support browser native form control (#5503)
<img width="552" alt="Screen Shot 2021-06-19 at 10 26 37 am" src="https://user-images.githubusercontent.com/10513364/122625517-d8030a00-d0e8-11eb-800b-af047c33d916.png">
export default function App() {
const { register, handleSubmit } = useForm({
shouldUseNativeValidation: true,
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register("firstName", {
required: "This is required from bill."
})}
autoComplete={"off"}
placeholder="First Name"
/>
<input type="submit" />
</form>
);
}
🐞 fix #5661 trigger throw error when the field is not found (#5664) 🐞 fix #5678 reset with keep default values with Controller (#5688)
useForm to enabled native browser validationconst { register, handleSubmit } = useForm({
shouldUseNativeValidation: true,
});
fix register input without defaultValue for useFieldArray
register input without defaultValue for useFieldArraydeprecate defaultValue as required prop for useFieldArray
defaultValue as required prop for useFieldArrayfunction Test() {
const { control, register } = useForm();
const { fields } = useFieldArray({
control,
name: "test",
});
return (
{fields.map((field, index) => (
<input
key={field.id} // important to include key with field's id
{...register(`test.${index}.value`)}
- defaultValue={field.value} // make sure to include defaultValue
/>
))}
);
}
🐞 fix #5627 defaultValue reset inconsistent with setValueAs
🐞 fix #5627 defaultValue reset inconsistent with setValueAs (#5634)
🐞 fix #5623 Controller unmount to skip validation
🐞 fix #5623 Controller unmount to skip validation (#5624)
🐞 fix #5611 should not update dirty fields with blur event (#5614) 🐞 fix #5612 trigger() always return true
🐞 fix #5611 should not update dirty fields with blur event (#5614) 🐞 fix #5612 trigger() always return true (#5613)
💼 close #5603 to enable nested field array register with useFieldArray 👁️ improve useController to only return focus method from ref (#5582) 🏛️ improv
💼 close #5603 to enable nested field array register with useFieldArray 👁️ improve useController to only return focus method from ref (#5582) 🏛️ improve useFieldArray when it's getting unmount and pulling the latest values (#5583)
useController no longer access input ref except focus event for focus management🐞 fix #5555 register deeply nested field append with strict mode (#5580) 🐛 fix #5566 should unregister inputs when useFieldArray unmounted (#5579) 🔢 f
🐞 fix #5555 register deeply nested field append with strict mode (#5580) 🐛 fix #5566 should unregister inputs when useFieldArray unmounted (#5579) 🔢 fix #5568 include . for a precise focus index lookup (#5578) 🕵️♀️ improve logic on register absent inputs (#5556) 👮 fix #5538 type error with setError shouldFocus
Nothing published for this version
🐛 fix #5527 resolver switching
🐛 fix #5527 resolver switching (#5530)
🐛 fix #5507 register with value issue
🐛 fix #5507 register with value issue (#5511)
🐛 fix #5499 issue with controller defaultValue overwrite defaultValues at useForm
🐛 fix #5499 issue with controller defaultValue overwrite defaultValues at useForm (#5500)
🐛 fix #5492 with empty reset with useController setting unmount attribute (#5493) 👌 improve logic on unmount input with ref callback (#5488) ⚜️ featur
🐛 fix #5492 with empty reset with useController setting unmount attribute (#5493) 👌 improve logic on unmount input with ref callback (#5488) ⚜️ feature: support shouldTouch with setValue (#5181) ♻️ useFieldArray improvement with register value API (#5473) 💖 feature: support register with value (#5380) 🍌 useFieldArray reduce function (#5471) 🪖 improve focus logic for field array (#5470) 🪢 improve register perf and remove duplicated logic (#5469) 🧯 close #5440 remove extra value update (#5442) 🔬 remove extra ref assign (#5433) 🪝 Improve isValid form State by removing extra internal formState (#5415)
setValue support shouldTouch to update formState touchFieldssetValue('firstName', 'value', { shouldTouch: true });
register now accept value as optionregister('firstName', { value: 'value' });
isValid will initialise as falseNothing published for this version
Nothing published for this version
🎓 only register absent fields with shouldUnregister: false
🎓 only register absent fields with shouldUnregister: false (#5418)
shouldUnregister: false should not shallow merge or register absent input fields from defaultValues`ts trigger('firstName', { shouldFocus: true}) `
🌟 feature: focus with trigger (#4960)
trigger('firstName', { shouldFocus: true})
🧼 bubble up the error when thrown in submit handler (#5391)
trigger support focus with error inputtrigger('inputName', { shouldFocus: true });
handleSubmit will throw error within the onSubmit callback🐞 close #5379 getFieldValueAs return undefined when supplied with undefined (#5381) 🐛 fix #5382 trigger() return incorrect valid state
🐞 close #5379 getFieldValueAs return undefined when supplied with undefined (#5381) 🐛 fix #5382 trigger() return incorrect valid state (#5383)
🐛 fix #5366 inline defaultValue validation (#5376) ⌨️ improve controller defaultValue prop type (#5375) 🏋🏻♀️ reduce logic with value check
🐛 fix #5366 inline defaultValue validation (#5376) ⌨️ improve controller defaultValue prop type (#5375) 🏋🏻♀️ reduce logic with value check (#5356)
🐞 fix #5361 remove check field logic for getFieldValues (#5363) 🐞 fix #5277 Update value when switch to enabled from initial disabled state (#5350) 📦
🐞 fix #5361 remove check field logic for getFieldValues (#5363) 🐞 fix #5277 Update value when switch to enabled from initial disabled state (#5350) 📦 update typescript and remove object define in useController (#5353)
Nothing published for this version
🐞 fix #5344 avoid re-register inputs (#5345) 👌 close #5283 useFieldArray issue with restrict mode (#5331) 🏋🏻♀️ save esm bundle size (#5327) ⌨️ accept
🐞 fix #5344 avoid re-register inputs (#5345) 👌 close #5283 useFieldArray issue with restrict mode (#5331) 🏋🏻♀️ save esm bundle size (#5327) ⌨️ accept readonly field path arrays (#5317)
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
🐛 fix #5300 issue with register absent deep nested object (#5302) 🕵️ close #5297 issue with get with empty path
🐛 fix #5300 issue with register absent deep nested object (#5302) 🕵️ close #5297 issue with get with empty path (#5298)
🐞 fix #5286 useWatch with fieldArray inconsistent behaviour
🐞 fix #5286 useWatch with fieldArray inconsistent behaviour (#5287)
🐞 fix #5276 setValue with null
🐞 fix #5276 setValue with null (#5278)
🐞 fix #5246 useFieldArray with nested arrays throws error (#5251) 🐞 fix #5262 register checkbox issue with absent register
🐞 fix #5246 useFieldArray with nested arrays throws error (#5251) 🐞 fix #5262 register checkbox issue with absent register (#5263)
🐞 fix #5234 should render error logic for valid state (#5235) ⌨️ fix missing-register-options-generic-type
🐞 fix #5234 should render error logic for valid state (#5235) ⌨️ fix missing-register-options-generic-type (#5238)
🐞 fix #5229 issue with ref becomes undefined (#5233) 🐎 improve multiple useWatch performances (#5210) 🏑 close #5208 useFieldArray nested object with R
🐞 fix #5229 issue with ref becomes undefined (#5233) 🐎 improve multiple useWatch performances (#5210) 🏑 close #5208 useFieldArray nested object with React.StrictMode (#5209)
Nothing published for this version
✨ useForm register absent defaultValues
✨ useForm register absent defaultValues (#5069)
const App = () => {
const { register, handleSubmit } = useForm({
defaultValues: {
test: { firstName: 'bill', lastName: 'luo' }
}
})
const onSubmit = (data) => {
// missing registered input will be included
console.log(data) // { test: { firstName: 'bill', lastName: 'luo' } }
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("test.firstName")} />
<button />
</form>
)
}
useForm will register missing inputs from defaultValuesconst App = () => {
const { register, handleSubmit } = useForm({
defaultValues: {
test: { firstName: 'bill', lastName: 'luo' },
},
});
const onSubmit = (data) => {
// missing registered input will be included
console.log(data); // { test: { firstName: 'bill', lastName: 'luo' } }
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('test.firstName')} />
<button />
</form>
);
};
🧠 close #5162 watch behavior with useFieldArray set to shouldUnregister: true
🧠 close #5162 watch behavior with useFieldArray set to shouldUnregister: true (#5173)
Nothing published for this version
Nothing published for this version
Nothing published for this version
⌨️ fix #5122 type issue with the message
⌨️ fix #5122 type issue with the message (#5123)
Nothing published for this version
🐞 fix #5096 clearErrors with empty arg
🐞 fix #5096 clearErrors with empty arg (#5098)
🐞 fix #5089 Correct setValue in case update value for field array name (#5092) ✨ close #3956 isSubmitSuccessful set to false when submit Promise faile
🐞 fix #5089 Correct setValue in case update value for field array name (#5092) ✨ close #3956 isSubmitSuccessful set to false when submit Promise failed (#5064) ✨ support validate with error messages (#5088) 👌 improve handleSubmit logic with data gather (#5067) ⌨️ fix #5065 useController ref type (#5066) ✨ feature: include mount attribute to register field to avoid validation for unmounted inputs (#4785)
isSubmitSuccessful will return false when handleSubmit callback failed with Error or Promise reject.shouldUnregister: falseNothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →