Skip to content

Validators

Validators provide contextual messages for errors in Control Fields.

A Validator can validate different types of data, and each Validator will have a set of validations. For example, a String Validator will be able to check the length of a string, meanwhile a Number Validator can check the maximum or minimum value. You can also create custom Validators, please check the section Creating a custom Validator to know more.

Let’s create a password input with a required Validator. Just add a validator property to a Control Field as shown below:

{
"form": [
{
"uid": "userPasswordInput",
"kind": "control",
"widget": "textinput",
"path": "user.password",
"validator": {
"type": "string",
"required": true
}
}
]
}

You can add more validators, triggering multiple errors if one or more of the requirements are not fulfilled. Let’s add validations for minimum length 8 characters, maximum length 20 characters and a pattern to check that the password has only letters and numbers.

{
"form": [
{
"uid": "userPasswordInput",
"kind": "control",
"widget": "textinput",
"path": "user.password",
"validator": {
"type": "string",
"required": true,
"minLength": 8,
"maxLength": 20,
"pattern": "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]+$"
}
}
]
}

Let’s give it a try!

By default, validators use Zod’s built-in error messages. You can override these by providing a messages property with custom error messages for each validation rule.

The messages property accepts a Record<string, Localizable> where each key corresponds to a validation rule and the value is either a plain string or a translation config object for i18n support.

{
"validator": {
"type": "string",
"required": true,
"minLength": 8,
"messages": {
"required": "Please enter your password",
"minLength": "Password must be at least 8 characters"
}
}
}
{
"validator": {
"type": "string",
"required": true,
"messages": {
"required": {
"key": "errors.password.required",
"default": "Please enter your password"
}
}
}
}

Here’s a more complete example with custom messages for different validator types:

Each validator type supports a specific set of validation rules. Below are the available message keys for each type.

PropertyMessage keyDescription
(type check)invalidValue is not a string
requiredrequiredEmpty string when required: true
minLengthminLengthString length is below the minimum
maxLengthmaxLengthString length exceeds the maximum
patternpatternString does not match the regex pattern
formatformatString does not match the format (email, url, uuid, hostname, ipv4, ipv6, date, time, date-time, duration)
enumenumValue is not one of the allowed values
constconstValue does not match the exact value
PropertyMessage keyDescription
(type check)invalidValue is not a number
requiredrequiredEmpty string when required: true
minimumminimumValue is below the minimum
maximummaximumValue exceeds the maximum
exclusiveMinimumexclusiveMinimumValue is not greater than the exclusive minimum
exclusiveMaximumexclusiveMaximumValue is not less than the exclusive maximum
multipleOfmultipleOfValue is not a multiple of the specified number
enumenumValue is not one of the allowed values
constconstValue does not match the exact value
PropertyMessage keyDescription
(type check)invalidValue is not a boolean
requiredrequiredEmpty string when required: true
constconstValue does not match the expected boolean value
PropertyMessage keyDescription
(type check)invalidValue is not an array
requiredrequiredArray is empty when required: true
minItemsminItemsArray has fewer items than the minimum
maxItemsmaxItemsArray has more items than the maximum

To create custom validators, implement functions that follow the CustomValidatorSchemaFn interface. For example, let’s create a validator that allows us to enter only certain names:

export const allowedNames: CustomValidatorSchemaFn = (names: string[]) =>
z.string().check(
z.superRefine((val, ctx) => {
if (val && names.includes(val) === false) {
ctx.addIssue({
code: 'custom',
message: `Name "${val}" not in ${names.map((name) => `"${name}"`).join(', ')}`,
input: val,
});
}
}),
);

Next, inform the form that it should use the custom validator when it encounters a specific key in the validator declaration within your JSON. To do this, create a CustomValidatorSchemas object and pass it to the form through its customValidators input.

import * as Core from '@golemui/core';
import { allowedNames } from './custom-validators/allowed-names';
const customValidators: Core.CustomValidatorSchemas = {
allowedNames,
};
export function FormPage() {
return (
<div>
<React.FormComponent
(...)
customValidators={customValidators}
/>
</div>
);
}

Finally, declare your validator:

{
{
kind: 'input',
widget: 'textinput',
path: 'user.name',
validator: { type: 'custom', allowedNames: ['John', 'Jane'] }
},
}

Type something different from John and Jane to trigger the validation error.

To allow users to define when field validation should run, set the validateOn: ValidateOn property at the form level.

type ValidateOn =
| 'eager'
| 'change'
| 'blur'
| 'submit'
| ('change' | 'blur' | 'submit')[];

When not set, the default validateOn behaviour is 'eager'.

  • 'change' : When the user interacts with the field and changes its value.
  • 'blur' : When the user has interacted with the field and leaves the field.
  • 'submit': When using ‘submit’, validation triggers when the ‘submit’ event is emitted. When that happens, all fields are also touched first.
{
widget: 'button',
label: 'Create User',
on: {
click: 'submit',
},
}
  • 'eager': Validates when any of the above happens.
import * as Core from '@golemui/core';
export function FormPage() {
return (
<div>
<React.FormComponent
(...)
validateOn="change"
/>
</div>
);
}