Validation
Validation runs on Submit or Continue click. No validation on blur.
Built-in rules
All rules are optional. Combine them freely.
{
"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"
}
}| Rule | Type | What it checks | Default message |
|---|---|---|---|
required | boolean | string | Not null, undefined, an empty string or an empty array. Checkbox and switch fields must be checked | "This field is required" |
minLength | number | String length or selected-item count >= value | "Must be at least N characters/items" |
maxLength | number | String length or selected-item count <= value | "Must be at most N characters/items" |
min | number | Number >= value | "Must be at least N" |
max | number | Number <= value | "Must be at most N" |
pattern | string | Matches regex | "Invalid format" |
matchField | string | Value equals another field's value | "Fields must match" |
validator | string | Custom 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.
[
{
"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.
<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><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:
{
"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
onStepValidateis 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
showcondition 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 theemailformat.
Next steps
- Server-side Validation: validate untrusted submissions
- Error Handling: display errors and handle loading states
- Multi-Step Forms: validate across multiple steps
- Definition Reference: full TypeScript types for all validation rules