Validation
Register rules with useFieldValidation. Validators run when the field value changes (and when optional dependencies change).
Basic rule
const REQUIRED = 'This field is required.';
useFieldValidation(
field,
React.useCallback((value: string) => (value.trim() === '' ? REQUIRED : null), []),
);
- Return
nullwhen valid - Return a
stringorReactNodefor the error message - Errors display when the field is
touched
Stable error references
Important: the returned error must be referentially stable when the outcome is unchanged.
Returning a new JSX element every run can cause re-render loops, especially on array fields:
// Avoid — new element every validation
useFieldValidation(field, (value) =>
value === '' ? <span>Required</span> : null,
);
Prefer module-level constants:
const REQUIRED_ERROR = 'Required.';
useFieldValidation(
field,
React.useCallback((value) => (value === '' ? REQUIRED_ERROR : null), []),
);
Wrap validators in React.useCallback with minimal dependencies.
Cross-field validation
Pass dependent field refs as the third argument:
useFieldValidation(
emailField,
React.useCallback((email, [confirm]) => {
return email === confirm ? null : 'Emails must match.';
}, []),
[confirmEmailField],
);
Formix revalidates when any dependency value changes.
Array-level rules
Validate the whole list (e.g. “at least one item”):
const NO_ITEMS = 'Add at least one item.';
useFieldValidation(
itemsField,
React.useCallback((items: List<Item>) => (items.isEmpty() ? NO_ITEMS : null), []),
);
Show list-level errors from the array field:
const { error, touched } = useField(itemsField);
{
touched && error != null ? <p role="alert">{error}</p> : null;
}
Per-row rules belong on row components; list-level rules on the array container.
Async validation
Use useAsyncFieldValidation or useAsyncFieldStateValidation when validation requires a network round-trip (e.g. checking username availability).
Submit gating
With validators registered, useForm().valid reflects the full form. Pair with modified on edit forms:
<button disabled={!modified || !valid}>Save</button>
Do not duplicate validation only in the save handler — the button would stay enabled while invalid.