Skip to content

Migrate V7 to V8 (BETA)

React Hook Form V8 migration guide.

Installation

npm install react-hook-form@beta

React Compiler

V8 adds first-class support for the React Compiler. No additional configuration is required — React Hook Form is now compatible out of the box.

Flat Field Array

V8's types support flat field arrays, allowing simpler data structures when working with useFieldArray.

// Before (V7) — each row had to be an object
useFieldArray({ name: "items" }) // items: { value: string }[]
// After (V8) — primitive values type-check
useFieldArray({ name: "items" }) // items: string[]

Note: as of the current beta, this is a types-only improvement — fields still spreads each entry with { ...field, key } internally, which does not behave as expected for primitive array items. Verify rendering behavior for a primitive items: string[] array before relying on this in production.

Breaking Changes

The following changes require updates to your existing code when upgrading from V7.

Input Ref

register's returned ref is unchanged — it's still a callback ref that receives the real DOM element. This change is about Controller and useController's field.ref instead: in V7, field.ref was a partial proxy object exposing only focus, select, setCustomValidity, and reportValidity. In V8, field.ref is the actual DOM element, so any method or property on the real node is available.

const { field } = useController({ name: "firstName", control })
// Before (V7) — field.ref only exposed a fixed set of methods
field.ref.appendChild // ❌ not available
// After (V8) — field.ref is the actual input element
field.ref.appendChild // ✅ available, like any other DOM node

See #12773 for details.

useFieldArray

Two changes affect useFieldArray:

id renamed to key

The internal render identifier has been renamed from id to key. Update any destructured field references:

const { fields, append } = useFieldArray({ name: "items" })
// fields[0].key is the unique render identifier (was fields[0].id)

Note: you can still append id or key as data — neither affects the render key:

append({
key: "custom-key", // stored as field data, does not override the render key
id: "custom-id", // stored as field data, does not override the render key
})
fields[0].key // unique id used for re-render (auto-generated)

keyName prop removed

The keyName option has been removed from useFieldArray. The render key is always key.

Watch Component

The <Watch /> component prop names has been renamed to name for consistency with the rest of the API.

// Before (V7)
<Watch names={["firstName", "lastName"]} />
// After (V8)
<Watch name={["firstName", "lastName"]} />

watch Callback API

The watch subscription callback overload's exported type (WatchObserver) has been removed from the public API — as of the current beta, the callback still works at runtime, but you lose the named type if you relied on importing it. Use subscribe instead going forward, since it's the supported, fully-typed API for this pattern.

// Before (V7)
watch((value, { name, type }) => console.log(value))
// After (V8) — use subscribe
subscribe({
formState: { values: true },
callback: ({ values }) => console.log(values),
})

setValue

As of the current beta, setValue still directly updates useFieldArray fields, unchanged from V7 — this section describes a planned change that has not yet shipped. If you need to overwrite an entire field array, the replace method from useFieldArray remains the recommended approach in both V7 and V8:

const { replace } = useFieldArray({ name: "items" })
replace(newItems)

See setValue and useFieldArray docs for details.

Thank you for your support

If you find React Hook Form to be useful in your project, please consider starring and supporting it.

Edit