Skip to content

formState

State of the form

formState: Object

This object contains information about the entire form state. It helps you to keep on track with the user's interaction with your form application.

Return

NameTypeDescription
isDirtyboolean

Set to true after the user modifies any of the inputs.

  • Make sure to provide all inputs' defaultValues at the useForm, so hook form can have a single source of truth to compare whether the form is dirty.

    const {
      formState: { isDirty, dirtyFields },
      setValue,
    } = useForm({ defaultValues: { test: "" } });
    
    // isDirty: true
    setValue('test', 'change')
     
    // isDirty: false because there getValues() === defaultValues
    setValue('test', '') 
    
  • File typed input will need to be managed at the app level due to the ability to cancel file selection and FileList object.

dirtyFieldsobject

An object with the user-modified fields. Make sure to provide all inputs' defaultValues via useForm, so the library can compare against the defaultValues.

Dirty fields will not represent as isDirty formState, because dirty fields are marked field dirty at field level rather the entire form. If you want to determine the entire form state use isDirty instead.

touchedFieldsobjectAn object containing all the inputs the user has interacted with.
defaultValuesobject

The value which has been set at useForm's defaultValues or updated defaultValues via reset API.

isSubmittedbooleanSet to true after the form is submitted. Will remain true until the reset method is invoked.
isSubmitSuccessfulboolean

Indicate the form was successfully submitted without any Promise rejection or Error been thrown within the handleSubmit callback.

isSubmittingbooleantrue if the form is currently being submitted. false otherwise.
isLoadingboolean

true if the form is currently loading async default values.

const { 
  formState: { isLoading } 
} = useForm({ 
  defaultValues: async () => await fetch('/api') 
});
submitCountnumberNumber of times the form was submitted.
isValidboolean
Set to true if the form doesn't have any errors.

setError has no effect on isValid formState, isValid will always derived via the entire form validation result.

isValidatingbooleanSet to true during validation.
errorsobjectAn object with field errors. There is also an ErrorMessage component to retrieve error message easily.

Rules

  • formState is wrapped with a defineProperty's get descriptor to improve render performance and skip extra logic if specific state is not subscribed to. Therefore make sure you invoke or read it before a render in order to enable the state update.

  • 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.

    useEffect(() => {
      if (formState.errors.firstName) {
        // do the your logic here
      }
    }, [formState]); // ✅ 
    // ❌ formState.errors will not trigger the useEffect        
    
    import { useForm } from "react-hook-form";
    
    export default function App () {
      const {
        register,
        handleSubmit,
        formState
      } = useForm();
    
      const onSubmit = (data) => console.log(data);
    
      React.useEffect(() => {
        console.log("touchedFields", formState.touchedFields);
      },[formState]); // use entire formState object as optional array arg in useEffect, not individual properties of it
    
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register("test")} />
          <input type="submit" />
        </form>
      );
    };
    
    import React from "react";
    import { useForm } from "react-hook-form";
    type FormInputs = {
      test: string
    }
    export default function App() {
      const {
        register,
        handleSubmit,
        formState
      } = useForm<FormInputs>();
      const onSubmit = (data: FormInputs) => console.log(data);
      
      React.useEffect(() => {
        console.log("touchedFields", formState.touchedFields);
      }, [formState]);
      
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register("test")} />
          <input type="submit" />
        </form>
      );
    }
    
  • Pay attention to the logical operator when subscription to formState.

    // ❌ formState.isValid is accessed conditionally, 
    // so the formState with get descriptor 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} />;
    

Examples

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const {
    register,
    handleSubmit,
    // Read the formState before render to subscribe the form state through the Proxy
    formState: { errors, isDirty, isSubmitting, touchedFields, submitCount },
  } = useForm();
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("test")} />
      <input type="submit" />
    </form>
  );
}
import { useForm } from "react-hook-form";

type FormInputs = {
  test: string
}

export default function App() {
  const {
    register,
    handleSubmit,
    // Read the formState before render to subscribe the form state through Proxy
    formState: { errors, isDirty, isSubmitting, touchedFields, submitCount },
  } = useForm<FormInputs>();
  const onSubmit = (data: FormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("test")} />
      <input type="submit" />
    </form>
  );
}

Video

The following video will explain in detail different form states.

Thank you for your support

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

Edit