Skip to main content
The useForm hook is the primary way to create and manage forms in TanStack Form for React. It returns an extended FormApi instance with React-specific utilities.

Import

import { useForm } from '@tanstack/react-form'

Signature

function useForm<TFormData>(opts?: FormOptions<TFormData>): ReactFormExtendedApi<TFormData>

Parameters

opts
FormOptions<TFormData>
Configuration options for the form
opts.defaultValues
TFormData
Initial values for the form fields
opts.formId
string
Unique identifier for the form (used for devtools and debugging)
opts.defaultState
Partial<FormState<TFormData>>
Default state for the form
opts.asyncAlways
boolean
If true, always run async validation even when sync validation fails. Defaults to false.
opts.asyncDebounceMs
number
Default debounce time in milliseconds for async validation
opts.canSubmitWhenInvalid
boolean
If true, allows form submission even when validation fails. Defaults to false.
opts.validators
FormValidators<TFormData>
Form-level validators
opts.validators.onMount
FormValidateOrFn<TFormData>
Validator that runs when the form mounts
opts.validators.onChange
FormValidateOrFn<TFormData>
Validator that runs when any field changes
opts.validators.onChangeAsync
FormAsyncValidateOrFn<TFormData>
Async validator that runs when any field changes
opts.validators.onChangeAsyncDebounceMs
number
Debounce time for onChange async validation
opts.validators.onBlur
FormValidateOrFn<TFormData>
Validator that runs when any field loses focus
opts.validators.onBlurAsync
FormAsyncValidateOrFn<TFormData>
Async validator that runs when any field loses focus
opts.validators.onBlurAsyncDebounceMs
number
Debounce time for onBlur async validation
opts.validators.onSubmit
FormValidateOrFn<TFormData>
Validator that runs on form submission
opts.validators.onSubmitAsync
FormAsyncValidateOrFn<TFormData>
Async validator that runs on form submission
opts.onSubmit
(props: { value: TFormData, formApi: FormApi }) => any | Promise<any>
Function called when the form is submitted with valid data
opts.onSubmitInvalid
(props: { value: TFormData, formApi: FormApi }) => void
Function called when the form is submitted with invalid data
opts.listeners
FormListeners<TFormData>
Form-level event listeners
opts.listeners.onChange
(props: { formApi, fieldApi }) => void
Called when any field value changes
opts.listeners.onBlur
(props: { formApi, fieldApi }) => void
Called when any field loses focus
opts.listeners.onMount
(props: { formApi }) => void
Called when the form mounts
opts.listeners.onSubmit
(props: { formApi, meta }) => void
Called when the form is submitted

Return Value

form
ReactFormExtendedApi<TFormData>
The form API instance with React-specific extensions
Field
FieldComponent<TFormData>
A React component for rendering form fields. It’s a pre-bound version of the standalone Field component.
Subscribe
Component
A React component for subscribing to form state changes
<form.Subscribe
  selector={(state) => [state.canSubmit, state.isSubmitting]}
  children={([canSubmit, isSubmitting]) => (
    <button disabled={!canSubmit}>{isSubmitting ? '...' : 'Submit'}</button>
  )}
/>
state
FormState<TFormData>
Current form state
values
TFormData
Current form field values
errors
ValidationError[]
Form-level validation errors
errorMap
ValidationErrorMap
Map of errors by validation event
fieldMeta
Record<string, FieldMeta>
Metadata for all form fields
isSubmitting
boolean
True when form submission is in progress
isSubmitted
boolean
True after form has been successfully submitted
isValidating
boolean
True when any validation is in progress
isValid
boolean
True when form and all fields are valid
canSubmit
boolean
True when form can be submitted based on current state
isDirty
boolean
True when any field has been modified
isPristine
boolean
True when no fields have been modified
isTouched
boolean
True when any field has been touched
isBlurred
boolean
True when any field has been blurred
submissionAttempts
number
Counter for submission attempts
handleSubmit
() => void
Submit the form. Triggers validation and calls onSubmit if valid.
reset
(values?: TFormData, opts?: { keepDefaultValues?: boolean }) => void
Reset the form to default values or specified values
validateAllFields
(cause: ValidationCause) => Promise<void>
Manually trigger validation on all fields
validateField
<TField extends DeepKeys<TFormData>>(field: TField, cause: ValidationCause) => ValidationError[]
Validate a specific field
getFieldValue
<TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>
Get the current value of a field
setFieldValue
<TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: UpdateMetaOptions) => void
Set the value of a field
getFieldMeta
<TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta
Get metadata for a field
setFieldMeta
<TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void
Set metadata for a field
pushFieldValue
<TField extends DeepKeys<TFormData>>(field: TField, value: any, opts?: UpdateMetaOptions) => void
Push a value to an array field
insertFieldValue
<TField extends DeepKeys<TFormData>>(field: TField, index: number, value: any, opts?: UpdateMetaOptions) => void
Insert a value into an array field at a specific index
removeFieldValue
<TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: UpdateMetaOptions) => void
Remove a value from an array field
swapFieldValues
<TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number) => void
Swap two values in an array field
replaceFieldValue
<TField extends DeepKeys<TFormData>>(field: TField, index: number, value: any, opts?: UpdateMetaOptions) => void
Replace a value in an array field

Usage

Basic Form

import { useForm } from '@tanstack/react-form'

function MyForm() {
  const form = useForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    onSubmit: async ({ value }) => {
      console.log('Form submitted:', value)
    },
  })

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        e.stopPropagation()
        form.handleSubmit()
      }}
    >
      <form.Field
        name="firstName"
        children={(field) => (
          <>
            <input
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.errors && (
              <em>{field.state.meta.errors.join(', ')}</em>
            )}
          </>
        )}
      />
      <button type="submit">Submit</button>
    </form>
  )
}

Form with Validation

import { useForm } from '@tanstack/react-form'

function MyForm() {
  const form = useForm({
    defaultValues: {
      email: '',
      age: 0,
    },
    validators: {
      onChange: ({ value }) => {
        if (value.age < 18) {
          return {
            fields: {
              age: 'Must be 18 or older',
            },
          }
        }
        return undefined
      },
    },
    onSubmit: async ({ value }) => {
      console.log('Form submitted:', value)
    },
  })

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        form.handleSubmit()
      }}
    >
      {/* Form fields */}
    </form>
  )
}

Using Subscribe Component

import { useForm } from '@tanstack/react-form'

function MyForm() {
  const form = useForm({
    defaultValues: { name: '' },
    onSubmit: async ({ value }) => console.log(value),
  })

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      form.handleSubmit()
    }}>
      <form.Subscribe
        selector={(state) => [state.canSubmit, state.isSubmitting]}
        children={([canSubmit, isSubmitting]) => (
          <button type="submit" disabled={!canSubmit}>
            {isSubmitting ? 'Submitting...' : 'Submit'}
          </button>
        )}
      />
    </form>
  )
}

TypeScript

import { useForm } from '@tanstack/react-form'

interface FormData {
  firstName: string
  lastName: string
  age: number
}

function MyForm() {
  const form = useForm<FormData>({
    defaultValues: {
      firstName: '',
      lastName: '',
      age: 0,
    },
    onSubmit: async ({ value }) => {
      // value is typed as FormData
      console.log(value.firstName)
    },
  })

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      form.handleSubmit()
    }}>
      {/* Form fields */}
    </form>
  )
}

Build docs developers (and LLMs) love