Skip to main content
The createField function creates and manages an individual form field in Solid applications.

Import

import { createField } from '@tanstack/solid-form'

Signature

function createField<
  TParentData,
  TName extends DeepKeys<TParentData>,
  TData extends DeepValue<TParentData, TName>,
  // ... additional type parameters
>(
  opts: () => CreateFieldOptions<TParentData, TName, TData, ...>
): () => FieldApi<TParentData, TName, TData, ...> & SolidFieldApi<...>

Parameters

opts
() => CreateFieldOptions<TParentData, TName, TData>
required
A function that returns the configuration object for the field. The function is reactive and will update the field when dependencies change.
opts().form
FormApi<TParentData>
required
The parent form API instance.
opts().name
TName
required
The field name as a path string (e.g., ‘user.firstName’ or ‘items[0].name’).
opts().defaultValue
TData
The default value for the field.
opts().validators
FieldValidators<TParentData, TName, TData>
Validation functions for the field.
opts().validators.onChange
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs on every change.
opts().validators.onChangeAsync
FieldAsyncValidateOrFn<TParentData, TName, TData>
Async validator that runs on change.
opts().validators.onBlur
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field loses focus.
opts().validators.onMount
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field is mounted.
opts().asyncDebounceMs
number
Debounce time in milliseconds for async validation. Default is 500ms.
opts().asyncAlways
boolean
If true, async validation runs even if sync validation fails.

Return Value

field
() => FieldApi<TParentData, TName, TData> & SolidFieldApi
An accessor function that returns the field API instance.
field().name
TName
The field name.
field().state
FieldState<TData>
The current field state.
field().state.value
TData
The current field value.
field().state.meta
FieldMeta
Field metadata.
field().state.meta.errors
string[]
Current validation errors.
field().state.meta.isTouched
boolean
Whether the field has been touched.
field().state.meta.isDirty
boolean
Whether the field value has changed.
field().state.meta.isValidating
boolean
Whether async validation is in progress.
field().handleChange
(value: TData) => void
Update the field value.
field().handleBlur
() => void
Mark the field as touched.
field().pushValue
(value: TData extends Array<infer U> ? U : never) => void
Add a value to an array field.
field().removeValue
(index: number) => void
Remove a value from an array field.
field().Field
FieldComponent
A nested Field component for creating sub-fields.

Usage Example

Basic Field

import { createForm, createField } from '@tanstack/solid-form'
import { Show } from 'solid-js'

interface FormData {
  email: string
}

function App() {
  const form = createForm<FormData>(() => ({
    defaultValues: {
      email: '',
    },
    onSubmit: ({ value }) => {
      console.log('Email:', value.email)
    },
  }))

  const emailField = form.createField(() => ({
    name: 'email',
    validators: {
      onChange: ({ value }) => {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
        return !emailRegex.test(value) ? 'Invalid email address' : undefined
      },
    },
  }))

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        form.handleSubmit()
      }}
    >
      <div>
        <label for={emailField().name}>Email:</label>
        <input
          id={emailField().name}
          name={emailField().name}
          value={emailField().state.value}
          onInput={(e) => emailField().handleChange(e.currentTarget.value)}
          onBlur={() => emailField().handleBlur()}
        />
        <Show when={emailField().state.meta.isTouched && emailField().state.meta.errors.length > 0}>
          <div style={{ color: 'red' }}>{emailField().state.meta.errors[0]}</div>
        </Show>
      </div>
      <button type="submit">Submit</button>
    </form>
  )
}

Field with Async Validation

import { createForm } from '@tanstack/solid-form'
import { Show } from 'solid-js'

function App() {
  const form = createForm(() => ({
    defaultValues: {
      username: '',
    },
    onSubmit: ({ value }) => {
      console.log(value)
    },
  }))

  const usernameField = form.createField(() => ({
    name: 'username',
    validators: {
      onChange: ({ value }) =>
        value.length < 3 ? 'Username must be at least 3 characters' : undefined,
      onChangeAsync: async ({ value }) => {
        await new Promise((resolve) => setTimeout(resolve, 1000))
        return value === 'taken' ? 'Username already taken' : undefined
      },
      onChangeAsyncDebounceMs: 500,
    },
  }))

  return (
    <div>
      <input
        value={usernameField().state.value}
        onInput={(e) => usernameField().handleChange(e.currentTarget.value)}
        onBlur={() => usernameField().handleBlur()}
      />
      <Show when={usernameField().state.meta.isValidating}>
        <p>Validating...</p>
      </Show>
      <Show when={usernameField().state.meta.errors.length > 0}>
        <div style={{ color: 'red' }}>{usernameField().state.meta.errors[0]}</div>
      </Show>
    </div>
  )
}

Nested Field

import { createForm } from '@tanstack/solid-form'

function App() {
  const form = createForm(() => ({
    defaultValues: {
      items: [] as Array<{ name: string }>,
    },
    onSubmit: ({ value }) => {
      console.log(value)
    },
  }))

  const itemsField = form.createField(() => ({
    name: 'items',
  }))

  return (
    <div>
      {/* Use itemsField().Field to create nested fields */}
      <itemsField.Field name="items[0].name">
        {(field) => (
          <input
            value={field().state.value}
            onInput={(e) => field().handleChange(e.currentTarget.value)}
          />
        )}
      </itemsField.Field>
    </div>
  )
}

See Also

Build docs developers (and LLMs) love