Capítulo 29 de 39

Chapter 29: useFieldArray

Core Idea

useFieldArray manages a dynamic list of inputs (add/remove/reorder rows) with better performance than manually mutating a plain array in state, by returning a fields array (each entry carrying an auto-generated id/key) plus mutation methods.

Key Concepts

  • name (required, static): the field-array's path; dynamic names are not supported, and each useFieldArray instance must be the only one using that name.
  • fields: array of { ...defaultValue, id } entries to .map() over; field.id (not index) must be used as the React key, otherwise reordering breaks which row's state belongs to which DOM node.
  • append / prepend / insert: add one or more entries (end / start / at an index); the object passed cannot be empty or partial — supply real default values for every field in the row.
  • swap / move: reposition entries without changing count.
  • update: replace one entry — causes that row's input to unmount and remount; use setValue instead if you don't want the remount.
  • replace: swap out the entire array's values at once.
  • remove: remove one entry, several by index array, or all entries if called with no argument.
  • rules: same validation shape as register (required, minLength, maxLength, validate); a validation error lands at formState.errors.<name>.root.
  • disabled (hook-level): disables the whole array — fields stays populated with every entry's disabled: true, all mutation methods become no-ops, but you still have to spread field.disabled onto each input yourself.

Code Examples

function FieldArray() {
  const { control, register } = useForm()
  const { fields, append, remove } = useFieldArray({ control, name: "test" })

  return (
    <>
      {fields.map((field, index) => (
        <input key={field.id} {...register(`test.${index}.value`)} />
      ))}
      <button type="button" onClick={() => append({ value: "" })}>append</button>
    </>
  )
}
  • What it demonstrates: the minimal loop — field.id as key, register with an index-templated path, append supplying a full default row.

Reference Tables

MethodSignatureNotes
append(obj | obj[], focusOptions?)adds at end, focuses new row by default
prepend(obj | obj[], focusOptions?)adds at start
insert(index, obj | obj[], focusOptions?)adds at position
swap(from, to)swap two positions
move(from, to)move one entry
update(index, obj)replaces one entry, remounts its row
replace(obj[])replaces the whole array
remove(index? | index[])removes one/many/all

Anti-patterns

  • Using index as the React key instead of field.id: breaks row identity across reorders/removals.
  • Stacking mutation calls in one handler (e.g. append() then remove(0) in the same click): can race against the array's own re-render; do the second mutation in a useEffect instead.
  • shouldUnregister: true with useFieldArray: not supported — the array relies on inputs mounting/unmounting to manage its own state, so newly appended fields get unregistered on re-render and lose their values.
  • Flat field arrays (test: ['a', 'b']): not supported — every entry must be an object (test: [{ value: 'a' }]).
  • Passing an empty or partial object to append/prepend/insert/update: must include real default values for every field in that row.

Key Takeaways

  1. Always key rows by field.id, never by array index.
  2. Field-array entries must be objects, and mutation calls need full default values, not partial/empty ones.
  3. update remounts the row (use setValue if you need an in-place value change without remounting); replace swaps the whole array.
  4. shouldUnregister: true and flat primitive arrays are both explicitly unsupported combinations.

Connects To

  • usefieldarray-fieldarray: the declarative component wrapper for this hook.
  • useform-register: how each row's individual inputs get registered, using the row index in the field name.
  • usewatch: commonly paired with useFieldArray to compute derived totals (e.g. cart price × quantity) from the current values.
  • usecontroller-controller: used instead of register for virtual/conditional rows inside a field array.