Capítulo 8 de 29

Chapter 8: Form Validation

Core Idea

Validators are configured per timing event (onChange/onBlur/onSubmit/onMount, each with an *Async counterpart), can live at the field level or the form level, and integrate directly with any Standard Schema library (Zod, Valibot, ArkType, Effect/Schema) without an adapter.

Key Concepts

  • Timing: choose exactly when a validator runs — real-time (onChange), on blur, on submit, or on mount — independently per validator.
  • Sync vs async: async validators (e.g. checking email uniqueness against a server) get built-in debouncing via asyncDebounceMs, so you don't hand-roll a debounce wrapper.
  • Schema-library integration: pass a Standard-Schema-compliant schema (Zod/Valibot/ArkType/Effect Schema) directly as a validator — no wrapper function needed.
  • Field-level vs form-level: field-level validators check one field in isolation; form-level validators (via useForm's own validators) are the natural place for cross-field or submit-time server checks.

Code Examples

<form.Field
  name="email"
  validators={{
    onChange: (value) => (!value.includes('@') ? 'Invalid email' : undefined),
    onBlurAsync: async (value) => {
      const exists = await checkEmailExists(value)
      return exists ? 'Email taken' : undefined
    },
  }}
  asyncDebounceMs={500}
>
  {(field) => (
    <>
      <input value={field.state.value} onChange={field.handleChange} />
      {field.state.meta.errors.map((err) => <span key={err}>{err}</span>)}
    </>
  )}
</form.Field>
  • What it demonstrates: a sync onChange validator combined with a debounced async onBlurAsync server check on the same field.

Reference Tables

TimingSync propAsync prop
MountonMount
ChangeonChangeonChangeAsync (+ onChangeAsyncDebounceMs)
BluronBluronBlurAsync (+ onBlurAsyncDebounceMs)
SubmitonSubmitonSubmitAsync

Key Takeaways

  1. Use onChangeAsync/onBlurAsync with asyncDebounceMs for anything that hits the network — don't debounce it yourself outside the field.
  2. A Zod/Valibot/ArkType schema can be passed straight into validators — check ch009 and the Standard Schema examples before writing a manual validator function for something a schema library already expresses.
  3. Cross-field checks (e.g. "confirm password") need onChangeListenTo/onBlurListenTo (ch013), not just a form-level validator — a form-level validator alone won't re-run when the other field changes.

Connects To

  • Ch009 Dynamic Validation: changing validation rules based on state (e.g. before/after first submit).
  • Ch013 Linked Fields: cross-field validation dependencies.
  • Ch016 Custom Errors: returning richer error shapes than a plain string.