getFieldState
Get validation and interaction state of a specific field.
</> getFieldState: UseFormGetFieldState Since v7.25.0
This method returns individual field state. It is useful for retrieving nested field state in a type-safe way.
Props
| Name | Type | Description |
|---|---|---|
name | string | registered field name. |
| formState | object | This is an optional prop, which is only required if formState has not been read from or subscribed to via useForm, useFormContext or useFormState. See the rules below for more information. |
Return
| Name | Type | Description |
|---|---|---|
isDirty | boolean | field is modified. Condition: subscribe to dirtyFields. |
| isTouched | boolean | field has received a focus and blur event. Condition: subscribe to touchedFields. |
| invalid | boolean | field is not valid. Condition: subscribe to errors. |
| isValidating | boolean | Since v7.51.0 field is currently being validated. Condition: subscribe to isValidating. |
| error | undefined | FieldError | field error object. Condition: subscribe to errors. |
- The prop must match a registered field name.
register("test") // registers the inputgetFieldState("test") // ✅ returns state for the registered fieldgetFieldState("non-existent-name") // ❌ returns default/false state since this name was never registered
getFieldStateworks by subscribing to form state updates, and you can subscribe to the formState in the following ways:-
You can subscribe at the
useForm,useFormContextoruseFormState. This is will establish the form state subscription andgetFieldStatesecond argument will no longer be required.const {register,formState: { isDirty },} = useForm()register("test")getFieldState("test") // ✅const { isDirty } = useFormState()register("test")getFieldState("test") // ✅const {register,formState: { isDirty },} = useFormContext()register("test")getFieldState("test") // ✅ -
When form state subscription is not set up, you can pass the entire
formStateas the second optional argument by following the example below:const { register } = useForm()register("test")const { isDirty } = getFieldState("test") // ❌ formState isDirty is not subscribed at useFormconst { register, formState } = useForm()const { isDirty } = getFieldState("test", formState) // ✅ formState.isDirty subscribedconst { formState } = useFormContext()const { isTouched } = getFieldState("test", formState) // ✅ formState.touchedFields subscribed
-
Examples
import { useForm } from "react-hook-form"export default function App() {const {register,getFieldState,formState: { isDirty, isValid },} = useForm({mode: "onChange",defaultValues: {firstName: "",},})// you can invoke before render or within the render functionconst fieldState = getFieldState("firstName")return (<form><input {...register("firstName", { required: true })} />{" "}<p>{getFieldState("firstName").isDirty && "dirty"}</p>{" "}<p>{getFieldState("firstName").isTouched && "touched"}</p><buttontype="button"onClick={() => console.log(getFieldState("firstName"))}>field state</button></form>)}
Tips
-
In a reusable field component, subscribe to only that field with
useFormState({ name }), and pass the result togetFieldState. The component then re-renders only when that field's state changes:function FieldError({ name }) {const { getFieldState } = useFormContext()const formState = useFormState({ name })const { error, isTouched } = getFieldState(name, formState)return isTouched && error ? <p role="alert">{error.message}</p> : null} -
getFieldStateis typed against your form values, so a misspelled nested path is a type error. Readingerrors.some.deep.pathby hand is not checked in the same way.
Troubleshooting
isDirty, isTouched, and invalid are always false
Why it happens
The field name may not be registered, or the component has not subscribed to the matching form-state properties. getFieldState reads state; it does not create every subscription by itself.
Fix
Check that the name exactly matches a registered field. Then read the required state from formState, or pass a scoped useFormState result:
const formState = useFormState({ control, name: "email" })const emailState = getFieldState("email", formState)
A field state is stale inside an event handler
Why it happens
The component never subscribed to the state being read. Reading getFieldState only inside a click handler does not cause the component to re-render when that state changes.
Fix
For reactive UI, call useFormState({ name }) during render and pass its result to getFieldState. For a one-time read, call getFieldState in the handler after the operation that updates the field.