Validation
VibeFlui runs validation before submit.
Validation happens in two layers:
- Built-in declarative field rules.
- Registry validators.
Built-in rules
| Property | Behavior |
|---|---|
required | Rejects empty string, null, undefined, and empty arrays. |
minLength | Checks string length. |
maxLength | Checks string length. |
min | Checks numeric value. |
max | Checks numeric value. |
pattern | Checks a JavaScript regular expression string. |
type: "email" | Checks email format. |
validationMessage | Overrides the built-in message for that field. |
TS
export const usersSchema = {
resource: "users",
form: {
fields: [
{
name: "name",
label: "Name",
type: "text",
required: true,
minLength: 2
},
{
name: "email",
label: "Email",
type: "email",
required: true
},
{
name: "age",
type: "number",
min: 18,
validationMessage: "Users must be at least 18 years old."
}
]
}
} as const;Validation messages
Built-in validation uses message keys such as:
fieldRequiredfieldMinLengthfieldMaxLengthfieldMinfieldMaxfieldPatternfieldEmail
The default message catalog also contains additional keys that custom validators can reuse. Only the keys above are used by the built-in field rules.
TS
messages: {
defaults: {
fieldRequired: "{label} is required.",
fieldMinLength: "{label} must be at least {min} characters."
}
}Registry validators
Use field.validator for custom logic.
TS
import { createRegistry } from "@vibeflui/core";
export const registry = createRegistry({
validators: {
"users.uniqueEmail": async (value) => {
if (String(value).endsWith("@example.test")) {
return "This test domain is not allowed.";
}
return undefined;
}
}
});TS
export const usersSchema = {
resource: "users",
form: {
fields: [
{
name: "email",
type: "email",
required: true,
validator: "users.uniqueEmail"
}
]
},
registry: {
validators: ["users.uniqueEmail"]
}
} as const;Wizard step validation
In wizard layouts, the Next button validates only fields assigned to the active step. Final submit validates the full form.