# formState

> Access real-time form state including isDirty, isValid, errors, isSubmitting, touchedFields, and dirtyFields from React Hook Form.

Source: https://react-hook-form.com/docs/useform/formstate

import formStateUseEffect from "@/components/codeExamples/formStateUseEffect"
import formStateUseEffectTs from "@/components/codeExamples/formStateUseEffectTs"
import formState from "@/components/codeExamples/formState"
import formStateTs from "@/components/codeExamples/formStateTs"

## `formState:` [FormState](https://react-hook-form.com/ts#FormState)

This object contains information about the entire form state. It helps you keep track of user interactions with your form application.

### Return

---

| Name                 | Type                         | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| -------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isDirty`            | `boolean` | Set to `true` after the user modifies any of the inputs.<ul><li>**Important:** make sure to provide all inputs' `defaultValues` in `useForm`, so hook form can have a single source of truth to determine whether the form is dirty.<pre><code>const {<br/>  formState: { isDirty, dirtyFields },<br/>  setValue<br/>} = useForm({ defaultValues: { test: "" } })<br/><br/>// isDirty: true ✅<br/>setValue('test', 'change')<br/><br/>// isDirty: false because getValues() === defaultValues ❌<br/>setValue('test', '')</code></pre></li><li>File-type inputs will need to be managed at the app level due to the ability to cancel file selection and [FileList](https://developer.mozilla.org/en-US/docs/Web/API/FileList) object.</li><li>Does not support custom objects, classes, or File objects.</li></ul> |
| `dirtyFields`        | `object`  | An object with the user-modified fields. Make sure to provide all inputs' `defaultValues` via `useForm`, so the library can compare against the `defaultValues.`<ul><li>**Important:** make sure to provide `defaultValues` in `useForm`, so hook form can have a single source of truth to compare each field's dirtiness.</li><li>Dirty fields do not necessarily represent the `isDirty` `formState`, because fields are marked as dirty at the field level rather than the entire form. If you want to determine the entire form state, use `isDirty` instead.</li></ul>                                                                                                                                                                                                                          |
| `touchedFields`      | `object`  | An object containing all the inputs the user has interacted with.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `defaultValues`      | `object`  | _(since v7.37.0)_ The value that was set at [useForm](https://react-hook-form.com/docs/useform)'s `defaultValues` or updated `defaultValues` via the [reset](https://react-hook-form.com/docs/useform/reset) API.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `isSubmitted`        | `boolean` | Set to `true` after the form is submitted. Will remain `true` until the `reset` method is invoked.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `isSubmitSuccessful` | `boolean` | Indicates that the form was successfully submitted without any runtime error.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `isSubmitting`       | `boolean` | `true` if the form is currently being submitted. `false` otherwise.<ul><li>It stays `true` only while the promise returned by your `onSubmit` handler is pending. If you start a request without `await`ing or returning it, `isSubmitting` turns `false` straight away.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `isLoading`          | `boolean` | _(since v7.41.0)_ `true` if the form is currently loading async default values.<ul><li>**Important:** this prop is only applicable to async `defaultValues`<pre><code>const {<br/>  formState: { isLoading }<br/>} = useForm({<br/>  defaultValues: async () =&gt; await fetch('/api')<br/>})</code></pre></li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `submitCount`        | `number`  | The number of times the form has been submitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `isValid`            | `boolean` | Set to `true` if the form doesn't have any errors.<ul><li>While `isValid` is subscribed, every field (or the whole [resolver](https://react-hook-form.com/docs/useform#resolver) schema) is validated on mount and on each change, whatever the [`mode`](https://react-hook-form.com/docs/useform#mode). `errors` only fills in according to `mode` and `reValidateMode`, so with the default `mode: "onSubmit"` `isValid` can be `false` while `errors` is still empty.</li><li>`isValid` is not computed while the form is [`disabled`](https://react-hook-form.com/docs/useform#disabled).</li><li>`setError` immediately forces `isValid` to `false`; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next `onChange`, submit, or `trigger()` call).</li></ul>                                                 |
| `isValidating`       | `boolean` | Set to `true` during validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `validatingFields`   | `object`  | _(since v7.51.0)_ Captures fields that are undergoing asynchronous validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `errors`             | `object`  | An object with field errors. There is also an [ErrorMessage](https://react-hook-form.com/docs/useformstate/errormessage) component to retrieve error messages easily.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `disabled`           | `boolean` | _(since v7.48.0)_ Set to `true` if the form is disabled via the `disabled` prop in [useForm](https://react-hook-form.com/docs/useform).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `isReady`            | `boolean` | _(since v7.56.0)_ Set to true when `formState` subscription setup is ready. <ul><li><p>Renders children before the parent completes setup. If you're using `useForm` methods (eg. `setValue`) in a child before the subscription is ready, it can cause issues. Use an `isReady` flag to ensure the form is initialized before updating state from the child.</p><pre><code>const {<br/> setValue,<br/> formState: { isReady }<br/>} = useForm();<br/><br/>// Parent component: ✅ <br/>useEffect(() =&gt; setValue('test', 'data'), []) <br/><br/>// Children component: ✅ <br/>useEffect(() =&gt; isReady &amp;&amp; setValue('test', 'data'), [isReady])</code></pre></li></ul>                                                                                                                                       |

> **Important: Rules**
>
> <ul>
>   <li>
>     Returned `formState` is wrapped with a
>     [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
>     to improve render performance and skip extra logic if a specific state is not
>     subscribed to. Therefore, make sure you invoke or read it before a render in
>     order to enable the state update.
>   </li>
>
>   <li>
>     `formState` is updated in batch. If you want to subscribe to `formState` via
>     `useEffect`, make sure that you place the entire `formState` in the optional
>     array.
>
>     <TabGroup buttonLabels={["snippet", "example"]}>
>
>     ```javascript
>     useEffect(() => {
>       if (formState.errors.firstName) {
>         // do your logic here
>       }
>     }, [formState]) // ✅
>     // ❌ [formState.errors] will not trigger the useEffect
>     ```
>
>     <CodeArea rawData={formStateUseEffect} tsRawData={formStateUseEffectTs}/>
>
>   </TabGroup>
>   </li>
>
>   <li>
>     Pay attention to the logical operator when subscribing to `formState`.
>
>     <CodeArea
>             rawData={`// ❌ formState.isValid is accessed conditionally,
>
> // so the Proxy does not subscribe to changes of that state
> return <button disabled={!formState.isDirty || !formState.isValid} />;
>
> // ✅ read all formState values to subscribe to changes
> const { isDirty, isValid } = formState;
> return <button disabled={!isDirty || !isValid} />;
> `}
> />
>
>   </li>
> </ul>
>

### Examples

---

<CodeArea
  rawData={formState}
  tsRawData={formStateTs}
  url="https://codesandbox.io/s/react-hook-form-v6-formstate-forked-tyqlp"
  tsUrl="https://codesandbox.io/s/react-hook-form-v6-ts-formstate-forked-5sxs3"
/>

### Video

---

[Watch on YouTube](https://www.youtube.com/watch?v=4kzd572NbkM)

### Tips

---

- Read `formState` in the component that uses it. To show a submit button's state without re-rendering the whole form, move the button into a child component that calls [`useFormState`](https://react-hook-form.com/docs/useformstate), or use the [`FormState`](https://react-hook-form.com/docs/useformstate/formstate) component.
- A common submit button pattern is `disabled={!isDirty || isSubmitting}`. It prevents an empty submission and a double submission. Remember to destructure both values before you use them.
- Use `isSubmitSuccessful` to show a confirmation message, or to reset the form in a `useEffect` after a successful submission (see [`reset`](https://react-hook-form.com/docs/useform/reset)).
- `errors` holds what is currently stored, not what is valid right now. To check validity without showing errors, use `isValid`. To validate on demand, use [`trigger`](https://react-hook-form.com/docs/useform/trigger).

### Troubleshooting

---

#### `isDirty` stays `true` after the user clears the field

**Why it happens:**

`isDirty` compares the current values with `defaultValues`. A field without a default value starts as `undefined`, and after the user types and deletes the text its value is `""`. That is not equal to `undefined`, so the field is still dirty.

**Fix:**

Give every field a default value in `useForm`, even if it is just `""`:

```javascript
useForm({ defaultValues: { firstName: "" } })
```

#### A `formState` value is stale when you read it in a handler

**Why it happens:**

`formState` only tracks the values your component reads during render. A value you read only inside an event handler or effect is never subscribed to.

**Fix:**

Destructure the value during render, as in `const { formState: { isValid } } = useForm()`. See [`useForm`](https://react-hook-form.com/docs/useform#formStateisValidoranothervalueisalwaysfalseinaneventhandler) for an example.
