Validation
validateFormData, error shape, touch behavior, and form-level errors.
Validation
The form system is designed to work with JSON Schema validation. Validate with validateFormData(schema, formData.getValues()), pass the returned errors to Form, and the Form component distributes them to the correct fields. Field errors only show when the field has been touched; on submit you can call touchForm() so all errors appear after a failed validation.
Flow
- Validate – Call
validateFormData(schema, formData.getValues())(e.g. from@/utils/web-validationor your own AJV setup). You get{ isValid, errors }. - Pass errors to Form – Use
errors(orerrors ?? undefined) as theerrorsprop on<Form>. - Match by field name – Form walks its children and, for any descendant with a
fieldprop, filterserrorsto those whoseinstancePath(with the leading/removed) equalsfield.name, then injects that slice as theerrorsprop. - Show messages – Each form field component shows validation messages when
field.touch && errors?.length > 0.
So: schema property names must match the field names you use in useFormDynamic and when calling getFields(). For example, if your schema has required: ["email"] and a property email, the field must be defined as email: "text" and used as field={email} so that the error with instancePath: "/email" is attached to that field.
validateFormData
If you use the provided utility (from @/utils/web-validation):
import { validateFormData } from "@/utils/web-validation";Signature: validateFormData(schema: object, data: unknown) => { isValid: boolean; errors: ErrorObject[] | null }
- schema – JSON Schema object (e.g.
type: "object",properties,required). The utility uses AJV withallErrors: true,$data: true, andajv-formats/ajv-errors. - data – Usually
formData.getValues().
Return:
- When valid:
{ isValid: true, errors: null }. - When invalid:
{ isValid: false, errors: ErrorObject[] }. Passerrors(orerrors ?? undefined) to<Form errors={formErrors}>.
AJV error shape
Errors are AJV ErrorObject. The Form component cares about:
| Property | Description |
|---|---|
instancePath | JSON Pointer to the invalid data, e.g. "/email" or "/name". Form matches the field by error.instancePath.slice(1) === field.name. |
message | Human-readable message; form field components render this in FormRadix.Message. |
Other properties (e.g. keyword, params) are available if you need them for custom UI or logging.
Important: Use property names in your schema that match your form field names so that instancePath lines up. Nested paths (e.g. "/address/city") would require a field name like "address/city" or custom error distribution logic.
Touch and submit
Field-level errors are only shown when the field has been touched (field.touch is true). That avoids showing errors for every field as soon as the user focuses the first one.
On submit:
- Run validation with
formData.getValues(). - If validation fails, call
formData.touchForm()so every field is marked touched; then seterrorson Form so all relevant messages appear. - Optionally set a form-level message with
formData.setError("...")and show it withFormAlert.
Example:
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
formData.setFetchStatus("loading");
const { isValid, errors } = validateFormData(schema, formData.getValues());
if (!isValid) {
formData.setFetchStatus("error");
formData.touchForm();
formData.setError("Please fix the errors below.");
return;
}
// Submit...
formData.setFetchStatus("success");
};
return (
<Form ref={formRef} onSubmit={handleSubmit} errors={errors ?? undefined} id="form-id">
<FormAlert formData={{ fetchStatus: formData.fetchStatus, error: formData.error }} />
{/* fields */}
</Form>
);Form-level error
Use the hook’s error and setError for messages that aren’t tied to a single field (e.g. “Please complete all required fields”, or a server error). Set it when validation fails or when the server returns an error:
formData.setError("Please complete all required fields.");Render it with FormAlert, passing the current status and error:
<FormAlert
formData={{
fetchStatus: formData.fetchStatus,
error: formData.error,
}}
/>FormAlert only renders when fetchStatus === "error" and formData.error is set. Clear the message when the user corrects the form or when you reset (e.g. formData.setError(null)).