Skip to content

Validation ​

Validation runs on Submit or Continue click. No validation on blur.

Built-in rules ​

All rules are optional. Combine them freely.

json
{
  "key": "password",
  "type": "password",
  "label": "Password",
  "validation": {
    "required": true,
    "minLength": 8,
    "minLengthMessage": "Password must be at least 8 characters",
    "pattern": "(?=.*[A-Z])(?=.*[0-9])",
    "patternMessage": "Must include a number and uppercase letter"
  }
}
RuleTypeWhat it checksDefault message
requiredboolean | stringNot null, undefined, an empty string or an empty array. Checkbox and switch fields must be checked"This field is required"
minLengthnumberString length or selected-item count >= value"Must be at least N characters/items"
maxLengthnumberString length or selected-item count <= value"Must be at most N characters/items"
minnumberNumber >= value"Must be at least N"
maxnumberNumber <= value"Must be at most N"
patternstringMatches regex"Invalid format"
matchFieldstringValue equals another field's value"Fields must match"
validatorstringCustom validator function returns null(your message)

pattern is not anchored: [0-9]{5} accepts abc12345x. Add ^ and $ to match the whole value.

Length, range, pattern, and matching rules have a corresponding ...Message field. For required, pass the message directly: required: "Please enter your name".

matchField ​

Useful for "confirm email" or "confirm password" fields. The values are compared the next time validation runs. matchField passes when the confirmation is empty, so keep required on it.

json
[
  {
    "key": "email",
    "type": "email",
    "label": "Email",
    "validation": { "required": true }
  },
  {
    "key": "confirmEmail",
    "type": "email",
    "label": "Confirm email",
    "validation": {
      "required": true,
      "matchField": "email",
      "matchFieldMessage": "Email addresses must match"
    }
  }
]

Custom validators ​

For synchronous business rules or format checks that cannot be expressed in JSON, register a validator function. Use async step validation for server requests.

vue
<script setup>
import { FormRenderer } from '@formhaus/vue';

function validateFormat(value, allValues) {
  if (!value) return null;
  return isValidFormat(value) ? null : 'Invalid format';
}
</script>

<template>
  <FormRenderer
    :definition="definition"
    :validators="{ checkFormat: validateFormat }"
    @submit="onSubmit"
  />
</template>
tsx
<FormRenderer
  definition={definition}
  validators={{ checkFormat: validateFormat }}
  onSubmit={handleSubmit}
/>

function validateFormat(value: unknown, allValues: Record<string, unknown>) {
  if (!value) return null;
  return isValidFormat(value as string) ? null : 'Invalid format';
}

Reference the validator by name in the definition:

json
{
  "key": "code",
  "type": "text",
  "label": "Code",
  "validation": { "validator": "checkFormat" }
}

When validation runs ​

  • On Continue. Visible fields on the current step are validated.
  • On Submit. Visible fields across all visible steps are validated, or all visible fields in a single-step form.
  • Async step validation. If onStepValidate is provided, it runs after sync validation passes but before the step transition. See Async Step Validation.
  • Never on blur. No errors while the user is still typing.
  • Hidden fields are skipped. If a show condition hides a field, it's not validated.
  • Empty non-required fields are skipped. minLength, pattern, etc. only run on non-empty values.
  • On the server. Run validateSubmission() on the posted values. The browser check does not verify value types or the email format.

Next steps ​

Formhaus is MIT licensed. Legal