Skip to content

useFormContext

Access useForm methods anywhere in the component tree.

</> useFormContext: () => UseFormReturn

This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would become inconvenient to pass the context as a prop.

Return


This hook returns all the useForm methods and props.

const methods = useForm()
<FormProvider {...methods} /> // all the useForm return props
const methods = useFormContext() // retrieve those props
RULES

You need to wrap your form with the FormProvider component for useFormContext to work properly.

Outside a FormProvider, useFormContext() returns null even though its return type isn't nullable, so destructuring it throws Cannot destructure property 'register' of ... as it is null.

To avoid repeating your form type, wrap it in a typed hook. The generic is only a type assertion; it isn't checked against the form the provider actually holds.

export const useProfileForm = () => useFormContext<ProfileFormValues>()

Since v7.44.0 Supports a third generic, useFormContext<TFieldValues, TContext, TTransformedValues>(), to type the resolver's transformed output separately from the raw form values and context type.

If you need to subscribe to form state values like errors, isDirty, or dirtyFields inside a FormProvider tree, use useFormState instead of destructuring formState from useFormContext(). formState is wrapped in a Proxy that only tracks the fields you actually read during render — destructuring formState and reading a property later (e.g. in a callback), or reading it conditionally, won't register a subscription, so the component won't re-render when that value changes.

Example


import { useEffect } from "react"
import { useForm, FormProvider, useFormContext } from "react-hook-form"
export default function App() {
const methods = useForm()
const onSubmit = (data) => console.log(data)
const { register, reset } = methods
useEffect(() => {
reset({
name: "data",
})
}, [reset]) // ❌ never put `methods` as a dependency
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input {...register("name")} />
<input type="submit" />
</form>
</FormProvider>
)
}
function NestedInput() {
const { register } = useFormContext() // retrieve all hook methods
return <input {...register("test")} />
}

Tips


  • Wrap components that use useFormContext in a small test helper, so you can render them alone in unit tests:

    import { render } from "@testing-library/react"
    function renderWithForm(ui: React.ReactElement, defaultValues = {}) {
    function Wrapper({ children }: { children: React.ReactNode }) {
    const methods = useForm({ defaultValues })
    return <FormProvider {...methods}>{children}</FormProvider>
    }
    return render(ui, { wrapper: Wrapper })
    }
  • useFormContext couples a component to one form shape. For an input that you reuse in many forms, take name (and optionally control) as props and use useController, or look at useLens.

  • Use the context only for the methods you need. For example, const { register } = useFormContext() in a leaf input. Read state with useFormState and values with useWatch.

Troubleshooting


Cannot destructure property 'register' of 'useFormContext(...)' as it is null

Why it happens

useFormContext() returns null outside a FormProvider. The exact wording depends on your bundler, but the message always ends in as it is null.

Fix

Render the component inside a FormProvider that receives the whole useForm return value:

const methods = useForm()
<FormProvider {...methods}>
<NestedInput />
</FormProvider>

handleSubmit is not a function (or another method is undefined)

Why it happens

Only some methods were passed to FormProvider, for example <FormProvider register={register}>. useFormContext() returns exactly what the provider received, so the other methods are missing.

Fix

Spread the whole useForm return value: <FormProvider {...methods}>.

A field inside a nested FormProvider is missing from the submitted data

Why it happens

useFormContext uses the nearest FormProvider. When one provider is rendered inside another, fields under the inner one register with the inner form, and the outer form's handleSubmit never sees them.

Fix

Use one FormProvider per form. To show two forms on one page, render the providers next to each other, not one inside the other.