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 propsconst methods = useFormContext() // retrieve those props
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 } = methodsuseEffect(() => {reset({name: "data",})}, [reset]) // ❌ never put `methods` as a dependencyreturn (<FormProvider {...methods}><form onSubmit={methods.handleSubmit(onSubmit)}><NestedInput /><input {...register("name")} /><input type="submit" /></form></FormProvider>)}function NestedInput() {const { register } = useFormContext() // retrieve all hook methodsreturn <input {...register("test")} />}
Tips
-
Wrap components that use
useFormContextin 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 })} -
useFormContextcouples a component to one form shape. For an input that you reuse in many forms, takename(and optionallycontrol) as props and useuseController, or look atuseLens. -
Use the context only for the methods you need. For example,
const { register } = useFormContext()in a leaf input. Read state withuseFormStateand values withuseWatch.
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.