CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tanstack--react-form

Powerful, type-safe forms for React.

Overview
Eval results
Files

field-api.mddocs/

Field Management

The FieldApi class provides granular control over individual form fields, including validation, metadata tracking, and array field operations. It integrates seamlessly with the FormApi to maintain field state and coordinate validation.

Capabilities

FieldApi Class

Core field management class for individual form field state and operations.

class FieldApi<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TFormOnMount extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnChange extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnBlur extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnDynamic extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TParentSubmitMeta = never,
> {
  /** Store instance for reactive state management */
  store: Store<FieldState<TData>>;

  /** Current field state */
  state: FieldState<TData>;

  /** Field name/path in the form data structure */
  name: TName;

  /** Parent form API instance */
  form: FormApi<TParentData, ...>;

  /** Field configuration options */
  options: FieldApiOptions<TParentData, TName, TData, ...>;

  constructor(opts: FieldApiOptions<TParentData, TName, TData, ...>);

  /**
   * Mounts the field and runs mount validation
   * @returns Cleanup function to unmount the field
   */
  mount(): () => void;

  /**
   * Updates field options with new configuration
   * @param opts - Partial field options to update
   */
  update(
    opts: FieldApiOptions<TParentData, TName, TData, ...>,
  ): void;

  /**
   * Gets the current field value
   * @deprecated Use `field.state.value` instead
   * @returns Current value of the field
   */
  getValue(): TData;

  /**
   * Sets the field value
   * @param updater - Function or value to set
   * @param opts - Options to control metadata updates and validation
   */
  setValue(
    updater: Updater<TData>,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Gets the field metadata
   * @returns Current field metadata
   */
  getMeta(): FieldMeta<TData, ...>;

  /**
   * Sets the field metadata
   * @param updater - Function or value to update metadata
   */
  setMeta(
    updater: Updater<FieldMeta<TData, ...>>,
  ): void;

  /**
   * Gets field information including instance and validation metadata
   * @returns Field info object
   */
  getInfo(): FieldInfo;

  /**
   * Pushes a value to the field (for array fields)
   * @param value - Value to push
   * @param opts - Options to control metadata updates and validation
   */
  pushValue(
    value: TData extends Array<infer U> ? U : never,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Inserts a value at a specific index (for array fields)
   * @param index - Index at which to insert
   * @param value - Value to insert
   * @param opts - Options to control metadata updates and validation
   */
  insertValue(
    index: number,
    value: TData extends Array<infer U> ? U : never,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Removes a value at a specific index (for array fields)
   * @param index - Index to remove
   * @param opts - Options to control metadata updates and validation
   */
  removeValue(
    index: number,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Replaces a value at a specific index (for array fields)
   * @param index - Index to replace
   * @param value - New value
   * @param opts - Options to control metadata updates and validation
   */
  replaceValue(
    index: number,
    value: TData extends Array<infer U> ? U : never,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Swaps two values in the array (for array fields)
   * @param aIndex - First index
   * @param bIndex - Second index
   * @param opts - Options to control metadata updates and validation
   */
  swapValues(
    aIndex: number,
    bIndex: number,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Moves a value from one index to another (for array fields)
   * @param aIndex - Source index
   * @param bIndex - Destination index
   * @param opts - Options to control metadata updates and validation
   */
  moveValue(
    aIndex: number,
    bIndex: number,
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Clears all values from the field (for array fields)
   * @param opts - Options to control metadata updates and validation
   */
  clearValues(
    opts?: UpdateMetaOptions,
  ): void;

  /**
   * Validates the field
   * @param cause - Reason for validation
   * @returns Promise that resolves to array of validation errors
   */
  validate(
    cause: ValidationCause,
  ): Promise<ValidationError[]>;

  /**
   * Updates the field's error map by merging with existing errors
   * @param errorMap - Validation error map to merge
   */
  setErrorMap(
    errorMap: ValidationErrorMap<
      UnwrapFieldValidateOrFn<TName, TOnMount, TFormOnMount>,
      UnwrapFieldValidateOrFn<TName, TOnChange, TFormOnChange>,
      UnwrapFieldAsyncValidateOrFn<TName, TOnChangeAsync, TFormOnChangeAsync>,
      UnwrapFieldValidateOrFn<TName, TOnBlur, TFormOnBlur>,
      UnwrapFieldAsyncValidateOrFn<TName, TOnBlurAsync, TFormOnBlurAsync>,
      UnwrapFieldValidateOrFn<TName, TOnSubmit, TFormOnSubmit>,
      UnwrapFieldAsyncValidateOrFn<TName, TOnSubmitAsync, TFormOnSubmitAsync>,
      UnwrapFieldValidateOrFn<TName, TOnDynamic, TFormOnDynamic>,
      UnwrapFieldAsyncValidateOrFn<TName, TOnDynamicAsync, TFormOnDynamicAsync>
    >,
  ): void;

  /**
   * Parses field value with a Standard Schema without setting internal errors
   * Useful for one-off validation checks without affecting field state
   * @param schema - The Standard Schema to validate against
   * @returns Validation error if any, undefined if valid
   */
  parseValueWithSchema(
    schema: StandardSchemaV1<TData, unknown>,
  ): ValidationError | undefined;

  /**
   * Async version of parseValueWithSchema
   * Parses field value with a Standard Schema without setting internal errors
   * @param schema - The Standard Schema to validate against
   * @returns Promise resolving to validation error if any, undefined if valid
   */
  parseValueWithSchemaAsync(
    schema: StandardSchemaV1<TData, unknown>,
  ): Promise<ValidationError | undefined>;

  /**
   * Handles field value change
   * @param updater - Function or value to set
   */
  handleChange(
    updater: Updater<TData>,
  ): void;

  /**
   * Handles field blur event
   */
  handleBlur(): void;
}

Field Component

React component for rendering fields with render prop pattern.

/**
 * A function component that takes field options and a render function as children
 * Uses the useField hook internally to manage the field instance
 *
 * @param props.name - Field path in the form data structure
 * @param props.validators - Field-level validators
 * @param props.children - Render function that receives the field API
 * @param props.defaultValue - Default value for the field
 * @param props.asyncDebounceMs - Debounce time for async validation
 * @param props.preserveValue - Whether to preserve value on unmount
 * @param props.mode - Rendering mode ('value' | 'array')
 * @returns ReactNode
 */
const Field: <
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
  TFormOnMount extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnChange extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnBlur extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnDynamic extends undefined | FormValidateOrFn<TParentData> = undefined,
  TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
  TParentSubmitMeta = never,
>({
  children,
  ...fieldOptions
}: FieldComponentProps<
  TParentData,
  TName,
  TData,
  TOnMount,
  TOnChange,
  TOnChangeAsync,
  TOnBlur,
  TOnBlurAsync,
  TOnSubmit,
  TOnSubmitAsync,
  TOnDynamic,
  TOnDynamicAsync,
  TFormOnMount,
  TFormOnChange,
  TFormOnChangeAsync,
  TFormOnBlur,
  TFormOnBlurAsync,
  TFormOnSubmit,
  TFormOnSubmitAsync,
  TFormOnDynamic,
  TFormOnDynamicAsync,
  TFormOnServer,
  TParentSubmitMeta
>) => ReactNode;

FieldOptions

Configuration options for creating a field instance.

interface FieldOptions<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
> {
  /** Field path in the form data structure (supports dot notation and array indices) */
  name: TName;

  /** Default value for the field */
  defaultValue?: TData;

  /** Debounce time in milliseconds for async validation */
  asyncDebounceMs?: number;

  /** Whether to always run async validation (even if sync validation fails) */
  asyncAlways?: boolean;

  /** Field-level validators */
  validators?: FieldValidators<
    TParentData,
    TName,
    TData,
    TOnMount,
    TOnChange,
    TOnChangeAsync,
    TOnBlur,
    TOnBlurAsync,
    TOnSubmit,
    TOnSubmitAsync,
    TOnDynamic,
    TOnDynamicAsync
  >;

  /** Whether to preserve field value when unmounted */
  preserveValue?: boolean;
}

interface FieldApiOptions<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnDynamic extends undefined | FormValidateOrFn<TParentData>,
  TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
  TParentSubmitMeta,
> extends FieldOptions<
    TParentData,
    TName,
    TData,
    TOnMount,
    TOnChange,
    TOnChangeAsync,
    TOnBlur,
    TOnBlurAsync,
    TOnSubmit,
    TOnSubmitAsync,
    TOnDynamic,
    TOnDynamicAsync
  > {
  /** Parent form API instance */
  form: FormApi<
    TParentData,
    TFormOnMount,
    TFormOnChange,
    TFormOnChangeAsync,
    TFormOnBlur,
    TFormOnBlurAsync,
    TFormOnSubmit,
    TFormOnSubmitAsync,
    TFormOnDynamic,
    TFormOnDynamicAsync,
    TFormOnServer,
    TParentSubmitMeta
  >;
}

UseFieldOptions

React hook-specific field options with mode support.

interface UseFieldOptions<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
  TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
  TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
  TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnDynamic extends undefined | FormValidateOrFn<TParentData>,
  TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
  TSubmitMeta,
> extends FieldApiOptions<
    TParentData,
    TName,
    TData,
    TOnMount,
    TOnChange,
    TOnChangeAsync,
    TOnBlur,
    TOnBlurAsync,
    TOnSubmit,
    TOnSubmitAsync,
    TOnDynamic,
    TOnDynamicAsync,
    TFormOnMount,
    TFormOnChange,
    TFormOnChangeAsync,
    TFormOnBlur,
    TFormOnBlurAsync,
    TFormOnSubmit,
    TFormOnSubmitAsync,
    TFormOnDynamic,
    TFormOnDynamicAsync,
    TFormOnServer,
    TSubmitMeta
  > {
  /**
   * Rendering mode for the field
   * - 'value': Standard value mode (default) - field re-renders on every value change
   * - 'array': Array mode with optimized re-rendering - use for array fields to prevent unnecessary re-renders of sibling array items when one item changes
   *
   * When using 'array' mode with array fields, child fields will only re-render when their specific index changes,
   * rather than re-rendering all items when any item in the array changes. This significantly improves performance
   * for forms with dynamic arrays of fields.
   *
   * Example usage:
   * ```typescript
   * <form.Field name="todos" mode="array">
   *   {(field) => (
   *     <div>
   *       {field.state.value.map((_, index) => (
   *         <form.Field key={index} name={`todos[${index}].text`}>
   *           {(subField) => <input value={subField.state.value} />}
   *         </form.Field>
   *       ))}
   *     </div>
   *   )}
   * </form.Field>
   * ```
   */
  mode?: 'value' | 'array';
}

interface UseFieldOptionsBound<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
> extends FieldOptions<
    TParentData,
    TName,
    TData,
    TOnMount,
    TOnChange,
    TOnChangeAsync,
    TOnBlur,
    TOnBlurAsync,
    TOnSubmit,
    TOnSubmitAsync,
    TOnDynamic,
    TOnDynamicAsync
  > {
  /** Rendering mode for the field */
  mode?: 'value' | 'array';
}

FieldState

Complete field state including value and metadata.

interface FieldState<TData> {
  /** Current field value */
  value: TData;

  /** Field metadata (base + derived) */
  meta: FieldMeta<TData, any, any, ...>;
}

type FieldMeta<TData, ...> = FieldMetaBase<TData, ...> & FieldMetaDerived<TData, ...>;

interface FieldMetaBase<TData, ...> {
  /** Whether the field has been touched (focused and then blurred) */
  isTouched: boolean;

  /** Whether the field has been blurred */
  isBlurred: boolean;

  /** Whether the field has been modified from its initial value */
  isDirty: boolean;

  /** Map of errors by validation trigger type */
  errorMap: ValidationErrorMap;

  /** Map tracking the source of errors (private) */
  errorSourceMap: ValidationErrorMapSource;

  /** Whether validation is currently in progress */
  isValidating: boolean;
}

interface FieldMetaDerived<TData, ...> {
  /** Array of validation errors for the field */
  errors: ValidationError[];

  /**
   * Array of validation errors only shown after field has been touched/blurred
   * This property may be undefined until the field has been interacted with
   */
  touchedErrors?: ValidationError[];

  /** Whether the field is in its initial state */
  isPristine: boolean;

  /** Whether the field is valid (no errors) */
  isValid: boolean;

  /** Whether the field's current value is the default value */
  isDefaultValue: boolean;
}

/** Type representing any FieldMeta */
type AnyFieldMeta = FieldMeta<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;

FieldValidators

Field-level validator configuration.

interface FieldValidators<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
  TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
  TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
> {
  /** Validator that runs when field is mounted */
  onMount?: TOnMount;

  /** Validator that runs when field value changes */
  onChange?: TOnChange;

  /** Async validator that runs when field value changes */
  onChangeAsync?: TOnChangeAsync;

  /** Validator that runs when field is blurred */
  onBlur?: TOnBlur;

  /** Async validator that runs when field is blurred */
  onBlurAsync?: TOnBlurAsync;

  /** Validator that runs on form submission */
  onSubmit?: TOnSubmit;

  /** Async validator that runs on form submission */
  onSubmitAsync?: TOnSubmitAsync;

  /** Validator that runs dynamically based on validation logic */
  onDynamic?: TOnDynamic;

  /** Async validator that runs dynamically based on validation logic */
  onDynamicAsync?: TOnDynamicAsync;

  /**
   * Array of field paths to listen to for changes
   * When any of these fields change, this field's onChange/onChangeAsync validators run
   */
  onChangeListenTo?: DeepKeys<TParentData>[];

  /**
   * Array of field paths to listen to for blur events
   * When any of these fields blur, this field's onBlur/onBlurAsync validators run
   */
  onBlurListenTo?: DeepKeys<TParentData>[];
}

FieldListeners

Event listener configuration for field lifecycle events.

interface FieldListeners<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  ...
> {
  /**
   * Listener called when field is mounted
   * @param fieldApi - Field API instance
   */
  onMount?: (
    fieldApi: FieldApi<TParentData, TName, TData, ...>,
  ) => void;

  /**
   * Listener called when field value changes
   * @param fieldApi - Field API instance
   */
  onChange?: (
    fieldApi: FieldApi<TParentData, TName, TData, ...>,
  ) => void;

  /**
   * Listener called when field is blurred
   * @param fieldApi - Field API instance
   */
  onBlur?: (
    fieldApi: FieldApi<TParentData, TName, TData, ...>,
  ) => void;

  /**
   * Listener called on form submission
   * @param fieldApi - Field API instance
   */
  onSubmit?: (
    fieldApi: FieldApi<TParentData, TName, TData, ...>,
  ) => void;
}

Field Validation Types

/**
 * Synchronous field validation function
 * @param props.value - Current field value
 * @param props.fieldApi - Field API instance
 * @returns Validation error or undefined if valid
 */
type FieldValidateFn<TParentData, TName, TData> = (props: {
  value: TData;
  fieldApi: FieldApi<TParentData, TName, TData, ...>;
}) => ValidationError | Promise<ValidationError>;

/**
 * Asynchronous field validation function with abort signal support
 * @param props.value - Current field value
 * @param props.signal - AbortSignal for cancellation
 * @param props.fieldApi - Field API instance
 * @returns Promise resolving to validation error or undefined if valid
 */
type FieldValidateAsyncFn<TParentData, TName, TData> = (props: {
  value: TData;
  signal: AbortSignal;
  fieldApi: FieldApi<TParentData, TName, TData, ...>;
}) => ValidationError | Promise<ValidationError>;

/** Union of validation function or Standard Schema validator */
type FieldValidateOrFn<TParentData, TName, TData> =
  | FieldValidateFn<TParentData, TName, TData>
  | StandardSchemaV1<TData, TData>;

/** Union of async validation function or Standard Schema validator */
type FieldAsyncValidateOrFn<TParentData, TName, TData> =
  | FieldValidateAsyncFn<TParentData, TName, TData>
  | StandardSchemaV1<TData, TData>;

Helper Types

/** Type representing any FieldApi instance */
type AnyFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;

/** Options for controlling metadata and validation updates */
interface UpdateMetaOptions {
  /** Skip metadata update */
  dontUpdateMeta?: boolean;

  /** Skip validation after update */
  dontValidate?: boolean;

  /** Skip running listeners */
  dontRunListeners?: boolean;
}

Helper Types

Type aliases for convenience when working with fields without needing to specify all generic parameters.

/**
 * FieldApi with all generics set to any for convenience in dynamic or loosely-typed contexts
 * Useful when you need to work with fields of unknown structure
 */
type AnyFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;

/**
 * FieldMeta with all generics set to any for convenience
 * Useful for generic field metadata handling
 */
type AnyFieldMeta = FieldMeta<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;

Usage Examples

Basic Field with Validation

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

function MyForm() {
  const form = useForm({
    defaultValues: {
      email: '',
    },
  });

  return (
    <form.Field
      name="email"
      validators={{
        onChange: ({ value }) => {
          if (!value.includes('@')) {
            return 'Invalid email address';
          }
          return undefined;
        },
        onChangeAsync: async ({ value, signal }) => {
          // Check email availability with server
          const response = await fetch(`/api/check-email?email=${value}`, { signal });
          const data = await response.json();
          return data.available ? undefined : 'Email already registered';
        },
      }}
    >
      {(field) => (
        <div>
          <label htmlFor={field.name}>Email:</label>
          <input
            id={field.name}
            value={field.state.value}
            onChange={(e) => field.handleChange(e.target.value)}
            onBlur={field.handleBlur}
          />
          {field.state.meta.isTouched && field.state.meta.errors.length > 0 && (
            <em>{field.state.meta.errors[0]}</em>
          )}
          {field.state.meta.isValidating && <span>Validating...</span>}
        </div>
      )}
    </form.Field>
  );
}

Array Field with Dynamic Items

function TodoListForm() {
  const form = useForm({
    defaultValues: {
      todos: [{ text: '', completed: false }],
    },
  });

  return (
    <form.Field name="todos" mode="array">
      {(field) => (
        <div>
          {field.state.value.map((_, index) => (
            <form.Field key={index} name={`todos[${index}].text`}>
              {(subField) => (
                <div>
                  <input
                    value={subField.state.value}
                    onChange={(e) => subField.handleChange(e.target.value)}
                  />
                  <button
                    type="button"
                    onClick={() => field.removeValue(index)}
                  >
                    Remove
                  </button>
                </div>
              )}
            </form.Field>
          ))}
          <button
            type="button"
            onClick={() => field.pushValue({ text: '', completed: false })}
          >
            Add Todo
          </button>
        </div>
      )}
    </form.Field>
  );
}

Field with Cross-Field Validation

function PasswordForm() {
  const form = useForm({
    defaultValues: {
      password: '',
      confirmPassword: '',
    },
  });

  return (
    <>
      <form.Field name="password">
        {(field) => (
          <input
            type="password"
            value={field.state.value}
            onChange={(e) => field.handleChange(e.target.value)}
          />
        )}
      </form.Field>

      <form.Field
        name="confirmPassword"
        validators={{
          onChangeListenTo: ['password'],
          onChange: ({ value, fieldApi }) => {
            const password = fieldApi.form.getFieldValue('password');
            if (value !== password) {
              return 'Passwords do not match';
            }
            return undefined;
          },
        }}
      >
        {(field) => (
          <div>
            <input
              type="password"
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.errors[0] && (
              <em>{field.state.meta.errors[0]}</em>
            )}
          </div>
        )}
      </form.Field>
    </>
  );
}

Using useField Hook Directly

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

function EmailInput() {
  const form = useForm({
    defaultValues: {
      email: '',
    },
  });

  const emailField = useField({
    form,
    name: 'email',
    validators: {
      onChange: ({ value }) => {
        if (!value.includes('@')) {
          return 'Invalid email';
        }
        return undefined;
      },
    },
  });

  return (
    <div>
      <input
        value={emailField.state.value}
        onChange={(e) => emailField.handleChange(e.target.value)}
        onBlur={emailField.handleBlur}
      />
      {emailField.state.meta.isTouched && emailField.state.meta.errors[0]}
    </div>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-tanstack--react-form

docs

advanced.md

field-api.md

form-api.md

framework-integrations.md

hooks.md

index.md

validation.md

tile.json