← All fault lines
Recurring07Dynamic formsWizards

Why useFieldArray gives up on wizards.

The limit is architectural, not a configuration you got wrong. Two field arrays cannot share a name — and a multi-step form is precisely the case that needs them to.

The rule that causes it

Each useFieldArray is unique and keeps its own state update. The documentation is explicit that you should not have multiple instances of it using the same name: each one handles a private snapshot of that array internally, so two of them pointed at the same path do not observe each other.

For a single-page form this never surfaces. For a wizard it surfaces immediately — step two and step four both need to append to the same collection, and each one is editing a copy that the other cannot see.

The three shapes where it bites

  • Multi-page forms. Different pages of a large form need to update the same field array, and there is no first-party way to share one instance across them.
  • Conditional rendering inside an array item. Uncontrolled inputs do not re-render when a value changes, so showing or hiding a nested field based on a sibling’s value simply does not react.
  • Arrays that mount and unmount. Toggled sections, wizard steps and tab navigation all remount the array, which is the scenario most likely to lose state in ways that look like a bug in your code.

What actually works today

For the conditional-field case there is a clean answer: extract the dependent field into its own component and use useWatch to subscribe to that exact array item at the component level. The subscription is narrow, so the re-render is narrow too — you get reactivity without giving up the uncontrolled model that made the library fast in the first place.

For sharing one array across steps there is no first-party answer. The pattern that circulates is to build your own React context and provide the result of the field array through it, so every step reads the same instance instead of instantiating a second one. It works. It is also a piece of state architecture you now own and maintain, in a library you adopted specifically so you would not have to.

No two useFieldArray for the same field can be instantiated, as each handles its own snapshot of state internally.react-hook-form · documented behaviour

Why it stays unsolved

This is not neglect. It follows from the design decision that makes the library worth using: state lives close to the inputs and is not centralised, which is what keeps re-renders cheap. Sharing one array across mounting boundaries asks for the opposite. The tension is real, and it is the reason the request keeps reopening rather than getting fixed.

Where we stand

We shipped a fix for this in @dashforge/[email protected]. useDashFieldArray no longer delegates the list to RHF’s useFieldArray: array identity is registered once per name on the form engine, and any later instance on that same name subscribes to that node instead of opening a private one.

In the wizard shape this page is about, that means two steps mounting useDashFieldArray('items') agree on the list. Same length, same ids, same order. An append made in step two is visible in step four, a remove or a move made in either is seen by the other, and a step mounting later adopts the list that already exists rather than starting its own. Those are the assertions of a characterization test that runs in CI, written so this paragraph cannot quietly go stale again. It did once: for five weeks it claimed the opposite of what the package shipped.

The tension above does not vanish, it moves. Bookkeeping kept local to each hook is part of what makes RHF’s re-renders cheap, and sharing one node across steps gives that up: a change in one step re-renders the other instances watching that name. For a wizard editing one collection, that is the entire point. The node is keyed by name, so lists that do not share a name do not share anything.