Last updated 17 days ago


Selectors are used to map the message resolvers to the relevant field(s).




Name-specific messages.


Type-specific messages.


General messages. Those are used as fallback values in case more specific messages are not present.

Each selector expects the map of the selector values and its resolvers:

type ValidationMessages = {
[selectorValue: string]: MessageResolver,


Resolver is an Object which maps the rule name to its actual message, or another resolver.

type MessageResolver = {
rule?: {
[ruleName: string]: Message,
type Message = string | ({ fieldProps, fields, form, ...extra }) => string

Generic states

There are multiple generic states provided within each resolver by default.

Generic state



Resolves for the empty required field.


Resolvers for the field with unexpected value when there are no higher specificity resolvers present.


Resolves for the asynchronous validation of the field.

Named resolvers

It is possible to define named resolvers corresponding to the named validation rules. These resolvers are defined under the rule key of the main resolver.

export default {
type: {
password: {
missing: 'Please provide the password',
invalid: 'The passwords is invalid',
rule: {
minLength: 'Password must be at least 6 characters long',

Note: Named resolver must have the corresponding validation rule with the same name in order to resolve. Otherwise, the closest validation message will be returned by the resolver. Considering the example above, if minLength rule doesn't exist and the type=["password"] field is invalid, the closest invalid resolver will be used (which is type.password.invalid in this case).


Whenever the resolver cannot resolve it would attempt to grab the closest resolver recursively and resolve it instead. Fallback resolvers are iterated by their specificity, which can be presented as follows:

Fallback sequence

  1. Named resolver for the same level selector.

  2. General resolver for the same level selector.

  3. General resolver for the next level selector.

  4. General resolver for the general selector.

Fallback example

Consider the following validation messages:

export default {
general: {
invalid: 'General invalid message',
type: {
email: {
invalid: 'E-mail is invalid',
name: {
userEmail: {
invalid: 'User e-mail is invalid',
rule: {
includesAt: 'E-mail must include "@" character',

And the following component layout:

value="foo" />

With the given scenario the includesAt validation rule would reject, marking the field as unexpected. This must be reflected in the UI using the validation messages schema.

This is the priority sequence in which resolvers will attempt to resolve the validation message:

  1. name.userEmail.rule.includesAt

  2. name.userEmail.invalid


  4. general.invalid

The validation message resolves as soon as the resolver returns the value. The same sequence applies for the type-specific named resolvers.