Capítulo 8 de 29
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.
onChange), on blur, on submit, or on mount — independently per validator.asyncDebounceMs, so you don't hand-roll a debounce wrapper.useForm's own validators) are the natural place for cross-field or submit-time server checks.<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>
onChange validator combined with a debounced async onBlurAsync server check on the same field.| Timing | Sync prop | Async prop |
|---|---|---|
| Mount | onMount | — |
| Change | onChange | onChangeAsync (+ onChangeAsyncDebounceMs) |
| Blur | onBlur | onBlurAsync (+ onBlurAsyncDebounceMs) |
| Submit | onSubmit | onSubmitAsync |
onChangeAsync/onBlurAsync with asyncDebounceMs for anything that hits the network — don't debounce it yourself outside the field.validators — check ch009 and the Standard Schema examples before writing a manual validator function for something a schema library already expresses.onChangeListenTo/onBlurListenTo (ch013), not just a form-level validator — a form-level validator alone won't re-run when the other field changes.