Schema FormX provides a built-in validation mechanism based on field configuration. This chapter explains how to configure validation rules and use custom validators.
Required validation checks for empty values based on field type:
Field Type
Empty Values
Default Error Message
string
null, undefined, ''
${label}不能为空
number
null, undefined, ''
${label}不能为空
boolean
null, undefined, ''
${label}不能为空
object
null, undefined, {}
${label}不能为空
array
null, undefined, []
${label}不能为空
Note: The default error message is hardcoded in Chinese (${label}不能为空, meaning "${label} cannot be empty"). To display English error messages, use a custom validator function instead of the required property.
Important Notes:
false for boolean type is NOT empty - required validation passes
0 for number type is NOT empty - required validation passes
Empty string '' is considered empty for all types
Hidden fields (hidden: true) still participate in validation — if a hidden field has required: true, it will still trigger validation errors. Use conditional required or a custom validator to skip validation for hidden fields
If you need more complex required validation, use a custom validator
Validation Logic Source Code:
// Core logic for required validationif ( required && (value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0) || isEqual(value, {}))) { errors[name] = `\${label}不能为空`; // Chinese: "\${label} cannot be empty"}
type Validator = (params: { value: unknown; // Current field value label: string; // Field display name data: Readonly<Record<string, unknown>>; // Entire form data}) => undefined | string | Promise<undefined | string>;
undefined means validation passed
string means validation failed, returning the error message
const schema = [ { type: 'string', name: 'password', label: 'Password', required: true, validator: ({ value, label }) => { if (!value) return `\${label} is required`; if (value.length < 8) return `\${label} must be at least 8 characters`; if (!/[A-Z]/.test(value)) return `\${label} must contain at least one uppercase letter`; if (!/[0-9]/.test(value)) return `\${label} must contain at least one digit`; if (!/[!@#$%^&*]/.test(value)) return `\${label} must contain at least one special character`; return undefined; } }];
{ type: 'string', name: 'username', label: 'Username', validator: ({ value, label }) => { if (!value) return `\${label} is required`; if (value.length < 3) return `\${label} must be at least 3 characters`; if (value.length > 20) return `\${label} must be at most 20 characters`; return undefined; }}
{ type: 'number', name: 'age', label: 'Age', validator: ({ value, label }) => { if (value === null || value === undefined) return `\${label} is required`; if (value < 0 || value > 120) return `\${label} must be between 0 and 120`; return undefined; }}
Note: The onChange callback only returns { data, name, value }, not errors. To get validation results in real-time, call the validate() method.
## Best Practices### Error Messages- Provide clear, actionable error messages- Include the field name for context- Use consistent error message format### Validation Logic- Keep validators focused on single validation concerns- Return `undefined` (not `null`) to indicate success- Provide specific error messages### Performance- Avoid synchronous operations in async validators- Use debouncing for expensive validation operations- Consider caching validation results## Related Documentation- [Form Configuration](/en/guide/basic/form-config) - Schema structure- [Custom Components](/en/guide/advanced/custom-field) - Custom form components- [Dependency Linkage](/en/guide/advanced/dependency) - Dependency linkage