Aura Design System

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

  1. Validate – Call validateFormData(schema, formData.getValues()) (e.g. from @/utils/web-validation or your own AJV setup). You get { isValid, errors }.
  2. Pass errors to Form – Use errors (or errors ?? undefined) as the errors prop on <Form>.
  3. Match by field name – Form walks its children and, for any descendant with a field prop, filters errors to those whose instancePath (with the leading / removed) equals field.name, then injects that slice as the errors prop.
  4. 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 with allErrors: true, $data: true, and ajv-formats / ajv-errors.
  • data – Usually formData.getValues().

Return:

  • When valid: { isValid: true, errors: null }.
  • When invalid: { isValid: false, errors: ErrorObject[] }. Pass errors (or errors ?? undefined) to <Form errors={formErrors}>.

AJV error shape

Errors are AJV ErrorObject. The Form component cares about:

PropertyDescription
instancePathJSON Pointer to the invalid data, e.g. "/email" or "/name". Form matches the field by error.instancePath.slice(1) === field.name.
messageHuman-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:

  1. Run validation with formData.getValues().
  2. If validation fails, call formData.touchForm() so every field is marked touched; then set errors on Form so all relevant messages appear.
  3. Optionally set a form-level message with formData.setError("...") and show it with FormAlert.

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)).