Common validator factories for @kin-form. Split out from
@kintools/form-core on purpose: validator wording and edge cases churn far
more than the engine does, so it's versioned separately.
npm add @kintools/form-validatorspnpm add @kintools/form-validatorsdeno add jsr:@kintools/form-validators| Validator | Flags |
|---|---|
required(message?) |
A missing value: null/undefined, an all-whitespace string, or an empty array. |
minLength(min, message?) |
A string or array shorter than min. |
maxLength(max, message?) |
A string or array longer than max. |
min(min, message?) |
A number smaller than min. |
max(max, message?) |
A number larger than max. |
url(message?) |
A non-empty string that isn't a valid URL (per the URL constructor). |
email(message?) |
A non-empty string that isn't a valid email address. |
pattern(regex, message?) |
A non-empty string that doesn't match regex. |
maxFileSize(bytes, message?) |
A File larger than bytes. |
password(options, message?) |
A non-empty string that doesn't meet every enabled rule. |
The format-specific validators (url, email, pattern) deliberately pass an
empty value through instead of also flagging it: combine them with required()
for a mandatory field, or use them alone for a field that's optional but must be
well-formed when present. maxFileSize passes through a missing file the same
way.
form.field("email", {
validators: [required("Email is required"), email("Enter a valid email")],
});Every factory's default message is its own name ("required", "minLength",
"maxLength", "min", "max", "url", "email", "pattern",
"maxFileSize", "password"), not an English sentence, so it doubles as a
stable i18n lookup key rather than display text:
{
field.invalid && field.touched && <span>{t(field.error)}</span>;
}Pass an explicit message to skip localization and show literal text instead,
e.g. required("This field is required").
Reports one shared message for any failing rule rather than identifying which
one: pair it with a requirements checklist in the UI (rendered from the same
options) rather than relying on message to explain what's missing:
form.field("password", {
validators: [
password({ minLength: 8, digit: true, upper: true, symbol: true }),
],
});| Option | Requires |
|---|---|
minLength |
Minimum string length. |
maxLength |
Maximum string length. |
digit |
At least one digit (0-9). |
upper |
At least one uppercase letter. |
lower |
At least one lowercase letter. |
symbol |
At least one non-alphanumeric character. |
A rule left undefined isn't checked.
Adapts any Standard Schema-compliant library (zod
v4+, valibot v1+, arktype, ...) into a SchemaValidator, for
whole-group/whole-form validation. Attach it as a group's or form's
schemaValidator, and it runs the group's own ~standard validation and
populates schemaErrorMap (a flat, dot-joined path -> message map) from the
result's issues:
import { z } from "zod";
import { toSchemaValidator } from "@kintools/form-validators";
const form = useForm({
initialValue: { email: "", password: "" },
schemaValidator: toSchemaValidator(signupSchema),
});Each issue's path becomes a key in that map (e.g. "address.line1"), so a
child field with that exact name can read its own slice of it via
field.schemaError, without any per-field wiring. Use it for whole-tree schema
validation; individual fields should keep using regular field validators
(required, pattern, a hand-written one, ...) rather than a schema of their
own. Mixing the two invites them to disagree.
- Validators guide
@kintools/form-core— the form engine these validators plug into