Capítulo 15 de 39

Chapter 15: register

Core Idea

register(name, options?) connects an input to the form — returns { name, ref, onChange, onBlur, ...(progressive attrs) } to spread onto the element, and configures HTML-standard-based validation rules for it.

Key Concepts

  • name is required and unique (except native radio/checkbox groups); dot syntax only for nested/array paths (test.0.firstName), bracket syntax (test[0].firstName) doesn't work.
  • Re-registering merges options, never replaces: calling register("test", {...}) again on the same name merges into the existing options — passing {} or undefined does not clear a previously set option; update the specific key instead (e.g. { required: false }).
  • Submission shape mirrors the name: register("firstName"){ firstName: value }; register("name.firstName"){ name: { firstName: value } }; register("name.firstName.0") → nests into an array.
  • Validation rules run per the form's mode, they don't block typing: required, min/max, minLength/maxLength, pattern are RHF-level rules, not native HTML attributes, unless progressive: true is set on useForm (then they're also reflected as real DOM attributes and on the returned object).
  • validate: a function (or named-function map for multiple independent checks) receiving the field value and, since 7.42.0, the full form values as a second argument; returns true/undefined (valid), a string (error message), or false (generic error). Recommended over the other rules for object/array field values.
  • valueAsNumber / valueAsDate / setValueAs: transform the raw input value before validation runs; mutually exclusive (setValueAs is ignored if either of the other two is true); none of them transform defaultValue/defaultValues.
  • disabled: input value becomes undefined and built-in validation rules are skipped entirely for that field.
  • deps: when this field's own validation runs (via its onChange/onBlur), also re-validate the listed fields — does not apply when validation is triggered manually via trigger().
  • Unmount doesn't auto-clean: values/refs for a registered input survive unmount unless you call unregister — RHF doesn't do this for you (except when shouldUnregister: true is set globally or per-field).
  • Reserved keywords: don't name a field ref or _f — conflicts with RHF's internal type checks.

Code Examples

<input {...register("firstName", { required: true })} placeholder="First name" />
<input {...register("lastName", { minLength: 2 })} placeholder="Last name" />
<input {...register("checkbox")} type="checkbox" value="A" />
<input {...register("radio")} type="radio" value="A" />
  • What it demonstrates: register works the same way across text, checkbox, and radio inputs — the value shape at submission depends on the input type, not on any extra config.
<input
  {...register("product", {
    validate: {
      checkAvailability: async (product, { category }) => {
        if (!category) return "Choose a category"
        if (!product) return "Specify your product"
        return (await checkProduct(category, product)) || "There is no such product"
      },
    },
  })}
/>
  • What it demonstrates: an async cross-field validate function reading a sibling field via the second argument.

Reference Tables

RuleApplies toNotes
requiredanyfor object/array values use validate instead
minLength / maxLengthstring-like
min / maxnumber-like
patternstringsa /g regex retains lastIndex state between calls
validateanythingfunction or named-function map; can be async, can read other field values
valueAsNumber / valueAsDate / setValueAsnumber/date/text inputspre-validation transforms, mutually exclusive
disabledanyvalue becomes undefined, skips built-in validation
depsanycross-triggers re-validation of listed fields

Anti-patterns

  • Using bracket syntax for array field names: register("test[0].firstName") doesn't work — always dot syntax.
  • Passing {} or undefined to a second register call expecting it to clear prior options: options merge, they don't reset; explicitly override the specific key.
  • Changing an input's name prop across renders: re-registers it as an entirely new field.
  • Relying on unmount to clean up a registered input's value: call unregister explicitly, or set shouldUnregister.

Key Takeaways

  1. register's validation rules run according to mode/reValidateMode — set progressive: true if you also want them as real HTML attributes.
  2. For object/array field values, use validate — the other rules are built for strings/numbers/booleans.
  3. Registered inputs don't clean up on unmount by default — unregister or shouldUnregister are the explicit opt-ins.

Connects To

  • useform-unregister: the manual cleanup counterpart.
  • useform-setvalue: needed when manually registering "virtual" inputs (e.g. custom components updated via useEffect).
  • usecontroller / usecontroller-controller: the controlled-input alternative to register.