This guide covers practical form validation patterns using PolyVal. Learn how to validate different field types, implement field comparisons, and create custom validation rules.
Basic field validation
String fields
Validate text input with length constraints and format requirements:
import { validate , SimpleValidationSchema } from 'polyval' ;
const userSchema : SimpleValidationSchema = {
username: {
type: 'string' ,
required: true ,
min: 3 ,
max: 20
},
email: {
type: 'string' ,
required: true ,
email: true
},
age: {
type: 'number' ,
min: 18
}
};
const userData = {
username: 'jo' ,
email: 'invalid-email' ,
age: 16
};
const errors = validate ( userSchema , userData , { lang: 'en' });
// Returns: [
// "Username: Must be at least 3 characters long",
// "Email: Invalid email address",
// "Age: Must be at least 18"
// ]
All validation rules are optional except type. Fields are optional by default unless you set required: true.
Number validation
Validate numeric inputs with minimum and maximum values:
const productSchema : SimpleValidationSchema = {
price: {
type: 'number' ,
required: true ,
min: 0
},
quantity: {
type: 'number' ,
required: true ,
min: 1 ,
max: 100
},
discount: {
type: 'number' ,
min: 0 ,
max: 100
}
};
Boolean validation
Validate checkboxes and toggle switches:
const termsSchema : SimpleValidationSchema = {
acceptTerms: {
type: 'boolean' ,
required: true ,
equals: true
},
newsletter: {
type: 'boolean'
}
};
const formData = {
acceptTerms: false ,
newsletter: true
};
const errors = validate ( termsSchema , formData , { lang: 'en' });
// Returns error because acceptTerms must be true
Use equals: true on boolean fields when you need to ensure a checkbox is checked, like for terms acceptance.
Field comparison validation
Validate fields relative to other fields in the form:
Password confirmation
const passwordSchema : SimpleValidationSchema = {
password: {
type: 'string' ,
required: true ,
min: 8
},
confirmPassword: {
type: 'string' ,
required: true ,
equals: 'password' // Must match the password field
}
};
const invalidData = {
password: 'secret123' ,
confirmPassword: 'different'
};
const errors = validate ( passwordSchema , invalidData , { lang: 'en' });
// Returns: ["ConfirmPassword: Must match the password field"]
Password change validation
Ensure new password differs from old password:
const changePasswordSchema : SimpleValidationSchema = {
oldPassword: {
type: 'string' ,
required: true
},
newPassword: {
type: 'string' ,
required: true ,
min: 8 ,
notEquals: 'oldPassword' // Must not match the old password
},
confirmNewPassword: {
type: 'string' ,
required: true ,
equals: 'newPassword'
}
};
Use notEquals to ensure a field value differs from another field, perfect for password changes or preventing duplicate entries.
Custom validation rules
Implement custom business logic with custom validators:
Reserved username check
const schemaWithCustomValidator : SimpleValidationSchema = {
username: {
type: 'string' ,
required: true ,
customValidators: [
{
validator : ( value ) => {
if ( value . toLowerCase () === 'admin' ) {
return 'Cannot use admin as username' ;
}
return undefined ;
},
messageKey: 'noAdminUsername'
}
]
}
};
const invalidData = {
username: 'admin'
};
const errors = validate ( schemaWithCustomValidator , invalidData , { lang: 'en' });
// Returns: ["Username: Cannot use admin as username"]
Custom validators should return undefined for valid values and an error message string for invalid values.
Cross-field validation
Access other field values in custom validators:
const schema : SimpleValidationSchema = {
startDate: {
type: 'date' ,
required: true
},
endDate: {
type: 'date' ,
required: true ,
customValidators: [
{
validator : ( value , data ) => {
if ( data . startDate && value < data . startDate ) {
return 'End date must be after start date' ;
}
return undefined ;
},
messageKey: 'endDateAfterStart'
}
]
}
};
The second parameter in custom validators provides access to all form data, enabling complex cross-field validation.
Customizing error messages
Type-level customization
Customize messages for all fields of a specific type: const errors = validate ( userSchema , userData , {
lang: 'en' ,
customMessages: {
string: {
min : ( min ) => `Minimum ${ min } characters required` ,
email: 'Email format is incorrect'
},
number: {
min : ( min ) => `Must be at least ${ min } `
}
}
});
Field-specific customization
Override messages for individual fields: const errors = validate ( userSchema , userData , {
lang: 'en' ,
customMessages: {
fields: {
username: {
min : ( min ) => `Username must be at least ${ min } characters long`
},
email: {
email: 'Please enter a valid email address'
}
}
}
});
Custom validator messages
Provide messages for custom validation rules: const errors = validate ( schema , data , {
lang: 'en' ,
customMessages: {
fields: {
username: {
noAdminUsername: "Sorry, 'admin' is a reserved username"
}
},
custom: {
endDateAfterStart: 'End date must be later than start date'
}
}
});
Message priority
Error messages are resolved in this order (highest to lowest priority):
Field-specific custom validator messages : customMessages.fields['fieldName']['customMessageKey']
Field-specific rule messages : customMessages.fields['fieldName']['min']
Global custom validator messages : customMessages.custom['messageKey']
Type-based rule messages : customMessages.string.email, customMessages.number.min
General error messages : customMessages.required, customMessages.invalid_type
Default language messages : Built-in English or Turkish messages
This priority system allows you to provide general customizations while overriding specific cases where needed.
Multilingual validation
PolyVal supports multiple languages out of the box:
const errors = validate ( userSchema , userData , { lang: 'en' });
// ["Username: Must be at least 3 characters long"]
Switch languages dynamically based on user preferences without changing your validation schema.
Real-time validation example
Implement real-time validation as users type:
import { useState , useEffect } from 'react' ;
import { validate } from 'polyval' ;
function FormField ({ name , schema } : { name : string ; schema : SimpleValidationSchema }) {
const [ value , setValue ] = useState ( '' );
const [ error , setError ] = useState < string | null >( null );
useEffect (() => {
// Debounce validation
const timer = setTimeout (() => {
const errors = validate ( schema , { [name]: value }, { lang: 'en' });
setError ( errors [ 0 ] || null );
}, 300 );
return () => clearTimeout ( timer );
}, [ value , name , schema ]);
return (
< div >
< input
value = { value }
onChange = {(e) => setValue (e.target.value)}
className = {error ? 'error' : '' }
/>
{ error && < span className = "error-message" > { error } </ span > }
</ div >
);
}
Advanced patterns
Regex validation
Use regular expressions for complex format validation:
const schema : SimpleValidationSchema = {
username: {
type: 'string' ,
required: true ,
regex: '^[a-zA-Z0-9_]+$' // Only alphanumeric and underscore
},
phone: {
type: 'string' ,
regex: '^ \\ +?[1-9] \\ d{1,14}$' // International phone format
},
zipCode: {
type: 'string' ,
regex: '^ \\ d{5}(- \\ d{4})?$' // US ZIP code
}
};
Strong password validation
Enforce complex password requirements:
const passwordSchema : SimpleValidationSchema = {
password: {
type: 'string' ,
required: true ,
min: 8 ,
// Must contain uppercase, lowercase, number, and special character
regex: '^(?=.*[a-z])(?=.*[A-Z])(?=.* \\ d)(?=.*[@$!%*?&])[A-Za-z \\ d@$!%*?&]+$'
}
};
Remember to escape backslashes in regex strings when using them in JavaScript/TypeScript.
Next steps
User registration See a complete registration form example
Validation rules Explore all available validation rules
Custom messages Deep dive into message customization
Custom validators Learn about custom validation functions