Skip to content

Typescript Support

List of exported Typescript Types.

Important: TypeScript 4.1 or above is required to work with React Hook Form — several exported types (recursive Path<T> field-name inference, template-literal field paths) rely on generics features introduced in that release, and older versions may not type-check correctly. React Hook Form itself is developed and tested against TypeScript 6.x.

Many of the types below share two generic parameters beyond the form values type (TFieldValues):

  • TContext — the type of the second argument passed to useForm's context option. It's forwarded, unchanged, to your resolver so validation logic can read values that live outside the form (e.g. a logged-in user, feature flags).
  • TTransformedValues — the shape of the data your resolver produces after transforming it (e.g. a Zod schema with .transform() that turns a string into a Date). It defaults to TFieldValues and only differs when a resolver reshapes data before it reaches handleSubmit's onValid callback.

</> Resolver

A Resolver lets you plug in an external validation library (such as Zod, Yup, or Joi) in place of React Hook Form's built-in validation. It receives the current form values and must return { values, errors }.

import { useForm, Resolver } from "react-hook-form"
type FormValues = {
firstName: string
lastName: string
}
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.firstName ? values : {},
errors: !values.firstName
? {
firstName: {
type: "required",
message: "This is required.",
},
}
: {},
}
}
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({ resolver })
const onSubmit = handleSubmit((data) => console.log(data))
return (
<form onSubmit={onSubmit}>
<input {...register("firstName")} placeholder="Bill" />
{errors?.firstName && <p>{errors.firstName.message}</p>}
<input {...register("lastName")} placeholder="Luo" />
<input type="submit" />
</form>
)
}

</> SubmitHandler

import { useForm, SubmitHandler } from "react-hook-form"
type FormValues = {
firstName: string
lastName: string
email: string
}
export default function App() {
const { register, handleSubmit } = useForm<FormValues>()
const onSubmit: SubmitHandler<FormValues> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<input type="email" {...register("email")} />
<input type="submit" />
</form>
)
}

</> SubmitErrorHandler

import { useForm, SubmitHandler, SubmitErrorHandler } from "react-hook-form"
type FormValues = {
firstName: string
lastName: string
email: string
}
export default function App() {
const { register, handleSubmit } = useForm<FormValues>()
const onSubmit: SubmitHandler<FormValues> = (data) => console.log(data)
const onError: SubmitErrorHandler<FormValues> = (errors) =>
console.log(errors)
return (
<form onSubmit={handleSubmit(onSubmit, onError)}>
<input {...register("firstName", { required: true })} />
<input {...register("lastName", { minLength: 2 })} />
<input type="email" {...register("email")} />
<input type="submit" />
</form>
)
}

</> FormSubmitHandler

The type for the Form component's onSubmit prop — unlike SubmitHandler, the payload includes the raw FormData and formDataJson from the submission.

export type FormSubmitHandler<TTransformedValues> = (payload: {
data: TTransformedValues
event?: React.BaseSyntheticEvent
formData: FormData
formDataJson: string
method?: "post" | "put" | "delete"
}) => unknown | Promise<unknown>

</> Control

import { useForm, useWatch, Control } from "react-hook-form"
type FormValues = {
firstName: string
lastName: string
}
function IsolateReRender({ control }: { control: Control<FormValues> }) {
const firstName = useWatch({
control,
name: "firstName",
defaultValue: "default",
})
return <div>{firstName}</div>
}
export default function App() {
const { register, control, handleSubmit } = useForm<FormValues>()
const onSubmit = handleSubmit((data) => console.log(data))
return (
<form onSubmit={onSubmit}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<IsolateReRender control={control} />
<input type="submit" />
</form>
)
}

</> UseFormReturn

export type UseFormReturn<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = {
watch: UseFormWatch<TFieldValues>
getValues: UseFormGetValues<TFieldValues>
getFieldState: UseFormGetFieldState<TFieldValues>
setError: UseFormSetError<TFieldValues>
clearErrors: UseFormClearErrors<TFieldValues>
setValue: UseFormSetValue<TFieldValues>
setValues: UseFormSetValues<TFieldValues>
trigger: UseFormTrigger<TFieldValues>
formState: FormState<TFieldValues>
resetField: UseFormResetField<TFieldValues>
reset: UseFormReset<TFieldValues>
resetDefaultValues: UseFormResetDefaultValues<TFieldValues>
handleSubmit: UseFormHandleSubmit<TFieldValues, TTransformedValues>
unregister: UseFormUnregister<TFieldValues>
control: Control<TFieldValues, TContext, TTransformedValues>
register: UseFormRegister<TFieldValues>
setFocus: UseFormSetFocus<TFieldValues>
subscribe: UseFormSubscribe<TFieldValues>
}
import {
DetailedHTMLProps,
InputHTMLAttributes,
ReactNode,
SelectHTMLAttributes,
forwardRef,
} from "react"
import type { FieldValues, UseFormReturn, SubmitHandler } from "react-hook-form"
import { useForm } from "react-hook-form"
type InputProps = DetailedHTMLProps<
InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => (
<input ref={ref} {...props} />
))
type Option = {
label: ReactNode
value: string | number | string[]
}
type SelectProps = DetailedHTMLProps<
SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
> & { options: Option[] }
const Select = forwardRef<HTMLSelectElement, SelectProps>(
({ options, ...props }, ref) => (
<select ref={ref} {...props}>
{options.map(({ label, value }) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
)
)
type FormProps<TFormValues extends FieldValues> = {
onSubmit: SubmitHandler<TFormValues>
children: (methods: UseFormReturn<TFormValues>) => ReactNode
}
const Form = <TFormValues extends FieldValues>({
onSubmit,
children,
}: FormProps<TFormValues>) => {
const methods = useForm<TFormValues>()
return (
<form onSubmit={methods.handleSubmit(onSubmit)}>{children(methods)}</form>
)
}
type FormValues = {
firstName: string
lastName: string
sex: string
}
export default function App() {
const onSubmit = (data: FormValues) => console.log(data)
return (
<Form<FormValues> onSubmit={onSubmit}>
{({ register }) => (
<>
<Input {...register("firstName")} />
<Input {...register("lastName")} />
<Select
{...register("sex")}
options={[
{ label: "Female", value: "female" },
{ label: "Male", value: "male" },
{ label: "Other", value: "other" },
]}
/>
<Input type="submit" />
</>
)}
</Form>
)
}

</> UseFormProps

export type UseFormProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = Partial<{
mode: Mode
disabled: boolean
reValidateMode: Exclude<Mode, "onTouched" | "all">
defaultValues: DefaultValues<TFieldValues> | AsyncDefaultValues<TFieldValues>
values: TFieldValues
errors: FieldErrors<TFieldValues>
resetOptions: Parameters<UseFormReset<TFieldValues>>[1]
resolver: Resolver<TFieldValues, TContext, TTransformedValues>
context: TContext
shouldFocusError: boolean
shouldUnregister: boolean
shouldUseNativeValidation: boolean
progressive: boolean
criteriaMode: CriteriaMode
delayError: number
formControl?: Omit<
UseFormReturn<TFieldValues, TContext, TTransformedValues>,
"formState"
>
validate: ValidateForm<TFieldValues>
}>

</> ValidateForm

The type of useForm's validate option — form-level validation that runs alongside (or instead of) a resolver.

export type ValidateFormEventType =
| "blur"
| "focusout"
| "change"
| "submit"
| "trigger"
| "valid"
export type ValidateForm<TFormValues extends FieldValues> = (props: {
formValues: TFormValues
formState: FormState<TFormValues>
eventType?: ValidateFormEventType
name?: FieldPath<TFormValues> | FieldPath<TFormValues>[]
}) => FormValidateResult<TFormValues> | Promise<FormValidateResult<TFormValues>>
export type FormValidateResult<TFormValues> =
| Partial<
Record<
keyof TFormValues,
{ message: string | string[] | boolean | undefined; type: string }
>
>
| string
| boolean

Return true for a valid form. Returning a string sets formState.errors.form; returning an object keyed by field name sets formState.errors.form.<fieldName> for each entry.


</> UseFormRegister

The type of register.

export type UseFormRegister<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: RegisterOptions<TFieldValues, TFieldName>
) => UseFormRegisterReturn<TFieldName>

</> UseFormRegisterReturn

The shape returned by invoking register. Useful for typing a custom Input component that spreads {...register(name)}.

export type UseFormRegisterReturn<
TFieldName extends InternalFieldName = InternalFieldName,
> = {
onChange: ChangeHandler
onBlur: ChangeHandler
ref: RefCallBack
name: TFieldName
min?: string | number
max?: string | number
maxLength?: number
minLength?: number
pattern?: string
required?: boolean
disabled?: boolean
}

</> UseFormUnregister

The type of unregister.

export type UseFormUnregister<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[],
options?: Omit<
KeepStateOptions,
| "keepIsSubmitted"
| "keepSubmitCount"
| "keepValues"
| "keepDefaultValues"
| "keepErrors"
> & { keepValue?: boolean; keepDefaultValue?: boolean; keepError?: boolean }
) => void

</> UseFormWatch

The type of watch. Overloaded — called with no argument, a single field name, an array of field names, or a callback for subscribing without triggering a re-render.

export type UseFormWatch<TFieldValues extends FieldValues> = {
(): TFieldValues
<TFieldNames extends readonly FieldPath<TFieldValues>[]>(
names: readonly [...TFieldNames],
defaultValue?: DeepPartial<TFieldValues>
): FieldPathValues<TFieldValues, TFieldNames>
<TFieldName extends FieldPath<TFieldValues>>(
name: TFieldName,
defaultValue?: FieldPathValue<TFieldValues, TFieldName>
): FieldPathValue<TFieldValues, TFieldName>
(
callback: WatchObserver<TFieldValues>,
defaultValues?: DeepPartial<TFieldValues>
): Subscription
}

</> UseFormSubscribe

The type of subscribe — subscribe to formState changes without triggering a component re-render.

export type UseFormSubscribe<TFieldValues extends FieldValues> = <
TFieldNames extends readonly FieldPath<TFieldValues>[],
>(payload: {
name?: readonly [...TFieldNames] | TFieldNames[number]
formState?: Partial<ReadFormState>
callback: (
data: Partial<FormState<TFieldValues>> & {
values: TFieldValues
name?: InternalFieldName
type?: EventType
}
) => void
exact?: boolean
}) => () => void

</> UseFormHandleSubmit

The type of the handleSubmit function.

export type UseFormHandleSubmit<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = (
onValid: SubmitHandler<TTransformedValues>,
onInvalid?: SubmitErrorHandler<TFieldValues>
) => (e?: React.BaseSyntheticEvent) => Promise<void>

</> UseFormReset

The type of the reset function.

export type UseFormReset<TFieldValues extends FieldValues> = (
values?:
| DefaultValues<TFieldValues>
| TFieldValues
| ResetAction<TFieldValues>,
keepStateOptions?: KeepStateOptions
) => void

</> UseFormResetField

The type of the resetField function.

export type UseFormResetField<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: ResetFieldConfig<TFieldValues, TFieldName>
) => void

</> UseFormResetDefaultValues

The type of the resetDefaultValues function.

export type UseFormResetDefaultValues<TFieldValues extends FieldValues> = (
values: DefaultValues<TFieldValues> | TFieldValues,
options?: Partial<Pick<KeepStateOptions, "keepDirty" | "keepIsValid">>
) => void

</> UseFormSetError

The type of the setError function.

export type UseFormSetError<TFieldValues extends FieldValues> = (
name:
| FieldPath<TFieldValues>
| `root.${string}`
| "root"
| "form"
| `form.${string}`,
error: ErrorOption,
options?: {
shouldFocus: boolean
}
) => void

</> UseFormClearErrors

The type of the clearErrors function.

export type UseFormClearErrors<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
| `root.${string}`
| "root"
| "form"
| `form.${string}`
) => void

</> UseFormSetValue

The type of the setValue function.

export type UseFormSetValue<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
value: FieldPathValue<TFieldValues, TFieldName>,
options?: SetValueConfig
) => void

</> UseFormSetValues

The type of the setValues function.

export type UseFormSetValues<TFieldValues extends FieldValues> = (
value: Partial<TFieldValues> | ((formValues: TFieldValues) => TFieldValues),
options?: SetValueConfig
) => void

</> UseFormSetFocus

The type of the setFocus function.

export type SetFocusOptions = Partial<{
shouldSelect: boolean
}>
export type UseFormSetFocus<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: SetFocusOptions
) => void

</> UseFormGetValues

The type of the getValues function. This type is overloaded; it can be called with no arguments, a single field name, or an array of field names.

export type GetValuesConfig = {
dirtyFields?: boolean
touchedFields?: boolean
}
export type UseFormGetValues<TFieldValues extends FieldValues> = {
(name?: undefined, config?: GetValuesConfig): TFieldValues
<TFieldName extends FieldPath<TFieldValues>>(
name: TFieldName,
config?: GetValuesConfig
): FieldPathValue<TFieldValues, TFieldName>
<TFieldNames extends FieldPath<TFieldValues>[]>(
names: readonly [...TFieldNames],
config?: GetValuesConfig
): [...FieldPathValues<TFieldValues, TFieldNames>]
}

</> UseFormGetFieldState

The type of the getFieldState function.

export type UseFormGetFieldState<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues>,
>(
name: TFieldName,
formState?: FormState<TFieldValues>
) => {
invalid: boolean
isDirty: boolean
isTouched: boolean
isValidating: boolean
error?: FieldError
}

</> UseFormTrigger

The type of the trigger function.

export type TriggerConfig = Partial<{
shouldFocus: boolean
}>
export type UseFormTrigger<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[],
options?: TriggerConfig
) => Promise<boolean>

</> UseWatchProps

The props accepted by the useWatch hook.

export type UseWatchProps<TFieldValues extends FieldValues = FieldValues> = {
defaultValue?: unknown
disabled?: boolean
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
control?: Control<TFieldValues>
exact?: boolean
compute?: (formValues: TFieldValues) => TFieldValues
}

</> WatchProps

The props for the Watch render-prop component.

export type WatchProps<
TFieldName extends WatchName<TFieldValues>,
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
TComputeValue = undefined,
> = {
control?: Control<TFieldValues, TContext, TTransformedValues>
name?: TFieldName
disabled?: boolean
exact?: boolean
defaultValue?: WatchDefaultValue<TFieldName, TFieldValues>
compute?: (value: WatchValue<TFieldName, TFieldValues>) => TComputeValue
render: (
value: WatchRenderValue<TFieldName, TFieldValues, TComputeValue>
) => React.ReactNode | React.ReactNode[]
}

</> UseFormStateProps

The props accepted by the useFormState hook.

export type UseFormStateProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = Partial<{
control?: Control<TFieldValues, any, TTransformedValues>
disabled?: boolean
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
exact?: boolean
}>

</> FormStateProps

The props for the FormState render-prop component.

export type FormStateProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = UseFormStateProps<TFieldValues, TTransformedValues> & {
render: (values: UseFormStateReturn<TFieldValues>) => React.ReactNode
}

</> FormProviderProps

export type FormProviderProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = {
children: React.ReactNode | React.ReactNode[]
} & UseFormReturn<TFieldValues, TContext, TTransformedValues>

</> FormProps

export type FormProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = Omit<React.FormHTMLAttributes<HTMLFormElement>, "onError" | "onSubmit"> &
Partial<{
control: Control<TFieldValues, any, TTransformedValues>
headers: Record<string, string>
validateStatus: (status: number) => boolean
onError: (
payload:
| { response: Response; error?: undefined }
| { response?: undefined; error: unknown }
) => void
onSuccess: ({ response }: { response: Response }) => void
onSubmit: FormSubmitHandler<TTransformedValues>
method: "post" | "put" | "delete"
children: React.ReactNode | React.ReactNode[]
render: (props: {
submit: (e?: React.FormEvent) => void
}) => React.ReactNode | React.ReactNode[]
encType:
| "application/x-www-form-urlencoded"
| "multipart/form-data"
| "text/plain"
| "application/json"
}>

</> UseFieldArrayReturn

export type UseFieldArrayReturn<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = {
swap: UseFieldArraySwap
move: UseFieldArrayMove
prepend: UseFieldArrayPrepend<TFieldValues, TFieldArrayName>
append: UseFieldArrayAppend<TFieldValues, TFieldArrayName>
remove: UseFieldArrayRemove
insert: UseFieldArrayInsert<TFieldValues, TFieldArrayName>
update: UseFieldArrayUpdate<TFieldValues, TFieldArrayName>
replace: UseFieldArrayReplace<TFieldValues, TFieldArrayName>
fields: (FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName> & {
disabled?: boolean
})[]
}

</> FieldArrayMethodProps

Focus options accepted as the second argument to the append, prepend, and insert functions.

export type FieldArrayMethodProps = {
shouldFocus?: boolean
focusIndex?: number
focusName?: string
}

</> UseFieldArrayProps

export type UseFieldArrayProps<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
TTransformedValues = TFieldValues,
> = {
name: TFieldArrayName
keyName?: TKeyName
control?: Control<TFieldValues, any, TTransformedValues>
rules?: {
validate?:
| Validate<FieldArray<TFieldValues, TFieldArrayName>[], TFieldValues>
| Record<
string,
Validate<FieldArray<TFieldValues, TFieldArrayName>[], TFieldValues>
>
} & Pick<
RegisterOptions<TFieldValues>,
"maxLength" | "minLength" | "required"
>
shouldUnregister?: boolean
disabled?: boolean
}

</> FieldArrayProps

export type FieldArrayProps<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = {
render: (
fieldArray: UseFieldArrayReturn<TFieldValues, TFieldArrayName, TKeyName>
) => React.ReactElement
} & UseFieldArrayProps<TFieldValues, TFieldArrayName, TKeyName>

</> ControllerRenderProps

The shape of the field object passed to Controller's render prop. Useful for typing a custom controlled input component.

export type ControllerRenderProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
onChange: (...event: any[]) => void
onBlur: Noop
value: FieldPathValue<TFieldValues, TName>
disabled?: boolean
name: TName
ref: RefCallBack
}

</> ControllerFieldState

The shape of the fieldState object passed to Controller's render prop.

export type ControllerFieldState = {
invalid: boolean
isTouched: boolean
isDirty: boolean
isValidating: boolean
error?: FieldError
}

</> UseControllerReturn

export type UseControllerReturn<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
field: ControllerRenderProps<TFieldValues, TName>
formState: UseFormStateReturn<TFieldValues>
fieldState: ControllerFieldState
}

</> UseControllerProps

export type UseControllerProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
TTransformedValues = TFieldValues,
> = {
name: TName
rules?: Omit<
RegisterOptions<TFieldValues, TName>,
"valueAsNumber" | "valueAsDate" | "setValueAs" | "disabled"
>
shouldUnregister?: boolean
defaultValue?: FieldPathValue<TFieldValues, TName>
control?: Control<TFieldValues, any, TTransformedValues>
disabled?: boolean
exact?: boolean
}

</> ControllerProps

export type ControllerProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
TTransformedValues = TFieldValues,
> = {
render: ({
field,
fieldState,
formState,
}: {
field: ControllerRenderProps<TFieldValues, TName>
fieldState: ControllerFieldState
formState: UseFormStateReturn<TFieldValues>
}) => React.ReactElement
} & UseControllerProps<TFieldValues, TName, TTransformedValues>

</> FieldError

export type FieldError = {
type: LiteralUnion<keyof RegisterOptions, string>
root?: FieldError
ref?: Ref
types?: MultipleFieldErrors
message?: Message
}

</> FieldErrors

export type FieldErrors<T extends FieldValues = FieldValues> = Partial<
FieldErrorsImpl<DeepRequired<T>>
> & {
root?: Record<string, GlobalError> & GlobalError
form?: GlobalError
}

</> ErrorOption

The error object accepted by setError.

export type ErrorOption = {
message?: Message
type?: LiteralUnion<keyof RegisterOptions, string>
types?: MultipleFieldErrors
}

</> Field

export type Field = {
_f: {
ref: Ref
name: InternalFieldName
refs?: HTMLInputElement[]
mount?: boolean
} & RegisterOptions
}

</> FieldPath

This type is useful when you define custom component's name prop, and it will type check against your field path.

export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>

</> FieldPathByValue

This type will return union with all available paths that match the passed value

export type FieldPathByValue<TFieldValues extends FieldValues, TValue> = {
[Key in FieldPath<TFieldValues>]: FieldPathValue<
TFieldValues,
Key
> extends TValue
? Key
: never
}[FieldPath<TFieldValues>]

</> FieldValues

export type FieldValues = Record<string, any>

</> FieldArrayWithId

export type FieldArrayWithId<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = FieldArray<TFieldValues, TFieldArrayName> & Record<TKeyName, string>

</> Mode

export type ValidationMode = typeof VALIDATION_MODE
export type Mode = keyof ValidationMode

</> RegisterOptions

export type RegisterOptions<
TFieldValues extends FieldValues = FieldValues,
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = Partial<{
required: Message | ValidationRule<boolean>
min: ValidationRule<number | string>
max: ValidationRule<number | string>
maxLength: ValidationRule<number>
minLength: ValidationRule<number>
validate:
| Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>
| Record<
string,
Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>
>
value: FieldPathValue<TFieldValues, TFieldName>
setValueAs: (value: any) => any
shouldUnregister?: boolean
onChange?: (event: any) => void
onBlur?: (event: any) => void
disabled: boolean
deps: FieldPath<TFieldValues> | FieldPath<TFieldValues>[]
}> &
(
| {
pattern?: ValidationRule<RegExp>
valueAsNumber?: false
valueAsDate?: false
}
| {
pattern?: undefined
valueAsNumber?: false
valueAsDate?: true
}
| {
pattern?: undefined
valueAsNumber?: true
valueAsDate?: false
}
)

</> FormState

The type of formState, returned by useForm and useFormState.

export type FormState<TFieldValues extends FieldValues> = {
isDirty: boolean
isLoading: boolean
isSubmitted: boolean
isSubmitSuccessful: boolean
isSubmitting: boolean
isValidating: boolean
isValid: boolean
disabled: boolean
submitCount: number
defaultValues?: undefined | Readonly<DeepPartial<TFieldValues>>
dirtyFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
touchedFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
validatingFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
errors: FieldErrors<TFieldValues>
isReady: boolean
}

</> KeepStateOptions

Options accepted by reset and useForm's resetOptions, controlling which parts of form state survive the reset.

export type KeepStateOptions = Partial<{
keepDirtyValues: boolean
keepErrors: boolean
keepDirty: boolean
keepValues: boolean
keepDefaultValues: boolean
keepIsSubmitted: boolean
keepIsSubmitSuccessful: boolean
keepTouched: boolean
keepIsValidating: boolean
keepIsValid: boolean
keepSubmitCount: boolean
keepFieldsRef: boolean
}>

</> FormStateProxy

export type FormStateProxy<TFieldValues extends FieldValues = FieldValues> = {
isDirty: boolean
isValidating: boolean
dirtyFields: FieldNamesMarkedBoolean<TFieldValues>
touchedFields: FieldNamesMarkedBoolean<TFieldValues>
validatingFields: FieldNamesMarkedBoolean<TFieldValues>
errors: boolean
isValid: boolean
}

</> NestedValue (Deprecated at 7.33.0)

NestedValue is deprecated and will be removed in a future major version. For a non-primitive value that needs to stay controlled (such as the array values below), type the field normally in your form values and wire it up with Controller instead of wrapping the type in NestedValue.

import { useEffect } from "react"
import { useForm, NestedValue } from "react-hook-form"
import { TextField, Select, MenuItem } from "@mui/material"
import { Autocomplete } from "@mui/material"
type Option = {
label: string
value: string
}
const options = [
{ label: "Chocolate", value: "chocolate" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Vanilla", value: "vanilla" },
]
export default function App() {
const {
register,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<{
autocomplete: NestedValue<Option[]>
select: NestedValue<number[]>
}>({
defaultValues: { autocomplete: [], select: [] },
})
const onSubmit = handleSubmit((data) => console.log(data))
useEffect(() => {
register("autocomplete", {
validate: (value) => value.length || "This is required.",
})
register("select", {
validate: (value) => value.length || "This is required.",
})
}, [register])
return (
<form onSubmit={onSubmit}>
<Autocomplete
options={options}
getOptionLabel={(option: Option) => option.label}
onChange={(e, options) => setValue("autocomplete", options)}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(errors?.autocomplete)}
helperText={errors?.autocomplete?.message}
/>
)}
/>
<Select
value=""
multiple
// cast required because NestedValue<number[]> bypasses field-level type inference
onChange={(e) => setValue("select", e.target.value as number[])}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
</Select>
<input type="submit" />
</form>
)
}
import { useForm, NestedValue } from "react-hook-form"
type FormValues = {
key1: string
key2: number
key3: NestedValue<{
key1: string
key2: number
}>
key4: NestedValue<string[]>
}
const {
formState: { errors },
} = useForm<FormValues>()
errors?.key1?.message // no type error
errors?.key2?.message // no type error
errors?.key3?.message // no type error
errors?.key4?.message // no type error

Thank you for your support

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

Edit