React Hook Form Documentation

repository·master·Indexed 20 days ago

https://github.com/react-hook-form/documentation

Documentation for React Hook Form, a library for building performant, flexible, and extensible forms in React with easy-to-use validation. Includes guides on core hooks like useForm, useController, and useFieldArray, as well as advanced patterns for accessibility (A11y), wizard forms, and performance optimization with FormProvider. Provides migration paths from v7 to v8 and detailed API references for form management methods.

Tokens
72.2K
Snippets
184
Records
233
Agent score
70%

What's inside React Hook Form

  1. Use `useLens` to scope form context to nested field paths

    master
    The useLens hook provides type-safe functional lenses that allow you to scope a React Hook Form context to a specific nested field path. This is useful for reducing boilerplate when working with deeply nested form structures, as it allows you to interact with a sub-section of the form as if it were the root.
  2. How to reset the form

    master

    There are two ways to clear form data:

    1. HTMLFormElement.reset(): A native browser method that clears input/select/checkbox values but does not interact with React Hook Form's internal state.
    2. React Hook Form reset() API: The recommended method. It resets all field values and clears all errors within the form state.
  3. Use the Form component for submission management

    master

    The <Form /> component (currently in BETA) is an optional component that handles form submission by aligning with standard native forms. It manages loading, error, and success states automatically.

    By default, it sends a POST request with form data as FormData. To use application/json instead, provide the headers prop. It supports both React Web and React Native and enables progressive enhancement in SSR frameworks.

    Key features:

    • Handles submission to a URL (action) or a callback (onSubmit).
    • Supports Server Actions (passing a function to action).
    • Provides lifecycle hooks: onSubmit, onSuccess, and onError.
    • Allows custom status code validation via validateStatus.
    <Form
      action="/api"
      method="post"
      onSubmit={() => {}}
      onSuccess={() => {}}
      onError={() => {}}
      validateStatus={(status) => status >= 200}
    />
  4. Rules and best practices for using `watch`

    master

    When using the watch API, keep the following rules in mind to avoid common pitfalls:

    • Default Values: If defaultValue is not provided as the second argument to watch, the first render will return undefined because the function is called before register. It is highly recommended to provide defaultValues to the useForm hook or pass an inline defaultValue to watch to ensure consistent behavior.
    • Re-renders: watch triggers a re-render at the root of your application or form. For better performance in large forms, use useWatch.
    • Dependency Tracking: The result of watch is optimized for the render phase. If you need to detect value updates inside a useEffect, you may need an external custom hook for value comparison.
    • Precedence: If both a defaultValue (as the second argument to watch) and defaultValues (in useForm) are supplied, the defaultValue passed to watch takes precedence.
  5. Register fields into the hook

    master

    To make a component's value available for validation and submission, you must register it into the hook. Each registered field requires a unique name as a key. You can use the spread operator to apply the registration to standard HTML elements like <input> or <select>.

    import { useForm, SubmitHandler } from "react-hook-form"
    
    interface IFormInput {
      firstName: string
      gender: "female" | "male" | "other"
    }
    
    export default function App() {
      const { register, handleSubmit } = useForm<IFormInput>()
      const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <label>First Name</label>
          <input {...register("firstName")} />
          
          <label>Gender Selection</label>
          <select {...register("gender")}>
            <option value="female">female</option>
            <option value="male">male</option>
            <option value="other">other</option>
          </select>
          
          <input type="submit" />
        </form>
      )
    }
  6. Important rules and behaviors of `setError`

    master

    When using setError, keep the following behaviors in mind:

    1. Validation Overwrites: setError will not persist an error if the input subsequently passes its associated register rules (e.g., minLength).
    2. Unregistered Fields: Errors set on fields that are not associated with an input (not registered) will persist until you manually call clearErrors.
    3. Form Validity: Calling setError forces formState.isValid to false. However, remember that isValid is always derived from the validation results of registered inputs or your schema.
    4. Focusing: The shouldFocus option will not work if the input is disabled.
  7. Mix controlled and uncontrolled components

    master

    React Hook Form is optimized for uncontrolled components using {...register}, but it is fully compatible with controlled components.

    • Uncontrolled Components: Use components that forward a ref (like a standard <input />) and register them directly with {...register('name')}.
    • Controlled Components: For UI library components that do not expose a native input ref (e.g., MUI Select, Antd Checkbox), wrap them with the Controller component. The Controller manages the component's state and provides a field object containing onChange, onBlur, value, and ref to be spread onto the component.

    You can mix both patterns within a single form.

    import { Input, Select, MenuItem } from "@material-ui/core"
    import { useForm, Controller } from "react-hook-form"
    
    const defaultValues = {
      select: "",
      input: "",
    }
    
    function App() {
      const { handleSubmit, reset, control, register } = useForm({
        defaultValues,
      })
      const onSubmit = (data) => console.log(data)
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <Controller
            render={({ field }) => (
              <Select {...field}>
                <MenuItem value={10}>Ten</MenuItem>
                <MenuItem value={20}>Twenty</MenuItem>
              </Select>
            )}
            control={control}
            name="select"
            defaultValue={10}
          />
    
          <Input {...register("input")} />
    
          <button type="button" onClick={() => reset({ ...defaultValues })}>
            Reset
          </button>
          <input type="submit" />
        </form>
      )
    }
  8. Compare watch, getValues, and local state

    master

    When accessing form values, choose the method based on whether you need to trigger a re-render:

    • watch: Subscribes to all inputs or specific input changes via an event listener. It triggers a re-render whenever the subscribed fields change. Use this when the UI needs to react to input changes.
    • getValues: Retrieves values stored inside the custom hook as a reference. It is fast and inexpensive because it does not trigger a re-render. Use this for logic that doesn't require immediate UI updates.
    • Local state: Standard React useState. This represents more than just input state and decides what to render, triggering a re-render on every change.
  9. Create a Smart Form Component

    master

    You can compose complex forms by creating a wrapper Form component that injects react-hook-form methods into its children via Children.map and createElement. This allows child components (like Input or Select) to receive register as a prop automatically if they have a name prop.

    Implementation Strategy:

    • The Form component uses useForm and iterates over children, injecting register into any child that possesses a name prop.
    • Custom input components receive register and spread it onto the native element.
    import { Children, createElement } from "react"
    import { useForm } from "react-hook-form"
    
    export default function Form({ defaultValues, children, onSubmit }) {
      const methods = useForm({ defaultValues })
      const { handleSubmit } = methods
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          {Children.map(children, (child) => {
            return child.props.name
              ? createElement(child.type, {
                  ...{ 
                    ...child.props, 
                    register: methods.register, 
                    key: child.props.name 
                  },
                })
              : child
          })}
        </form>
      )
    }
    
    export function Input({ register, name, ...rest }) {
      return <input {...register(name)} {...rest} />
    }
  10. Use `useFormState` to isolate re-renders

    master

    The useFormState hook allows you to subscribe to specific parts of the form state. By using this hook instead of destructuring formState from useForm, you can isolate re-renders to the component level where the hook is called, which is highly beneficial for performance in large, complex forms.

    Important Subscription Rule: To enable the subscription and ensure the component re-renders when the state changes, you must destructure the specific properties you need from the returned object. Reading the whole object without destructuring will fail to trigger updates.

    // ✅ Correct: Destructuring enables subscription
    const { isDirty } = useFormState();
    
    // ❌ Incorrect: This will not trigger re-renders on state changes
    const formState = useFormState();
  11. Understand React Hook Form design and philosophy

    master

    React Hook Form is designed to optimize both user and developer experience through several core principles:

    Performance Enhancements

    • Form state subscription model: Uses a proxy to allow fine-grained subscriptions to form state.
    • Minimized computation: Avoids unnecessary calculations during form interactions.
    • Isolated re-rendering: Limits component re-renders to only the parts of the UI that actually need to update.

    Developer Experience

    • Built-in validation: Closely aligned with HTML standards.
    • Extensibility: Supports powerful custom validation methods and native integration with schema validation libraries.
    • Type Safety: Strong TypeScript support provides early build-time feedback for robust form solutions.
  12. Note on Deprecated NestedValue type

    master

    The NestedValue type is deprecated as of version 7.33.0. Developers should avoid using it in new codebases. It was previously used to help type-check nested structures within useForm generic arguments, but modern TypeScript patterns in React Hook Form provide better alternatives for handling nested field paths and values.

    // Deprecated at 7.33.0
    import { useForm, NestedValue } from "react-hook-form"
    
    type FormValues = {
      key4: NestedValue<string[]>
    }
    
    const { formState: { errors } } = useForm<FormValues>()