> For the complete documentation index, see [llms.txt](https://docs.kasko.io/kasko-frontend-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kasko.io/kasko-frontend-documentation/core-concepts/validation/custom-validators.md).

# Adding custom validators

Naturally, it is necessary to add custom validation rules.

> **IMPORTANT:** keep in mind that the validation rules built in the frontend will **not** be used in backend (if not implemented there as well).

Every custom validation rule config must be defined in `validators` array in root plugin registration object.

```diff
 window.registerKaskoPlugin('kasko-plugin', (kasko) => {
   return {
+    validators: [...],
   };
 });
```

It can contain multiple validation rules, but none of the same `name`. Framework will throw error if already existing validation rule with same `name` is present.

Every validation rule must have `name` property and `validate` method.

```js
{
  name: 'rule_name',
  validate: () => null,
}
```

## `name: string`

Name as unique `string` that will be used in field definition (i.e `only_emoji_chars_allowed`).

## `validate: (context: PluginValidationContext) => (null | Record<string, boolean>)`

Validation function that has one argument and returns either `null` or `object` with validation rule names that contains errors (e.g. `{ at_least_n_chars_present_in: true }` means current field will have error `at_least_n_chars_present_in` and `null` means no error).

```ts
PluginValidationContext {
  fieldName: string;
  value: string | undefined;
  args: string[];
  disabled: boolean;
}
```

Note that `PluginValidationContext` doesn't return any other form values.

If there's a need to access input state or form state inside validator (e.g. to validate current field based on other fields/input value), then you can expose `kasko` service to validator function and access state or form values using available methods: `getState` or `getFormState`.

```diff
 {
   name: 'rule_name',
-  validate: customValidation,
+  validate: customValidation(kasko),
 }
```

```diff
- const customValidation = ({ value }) => {
+ const customValidation = (kasko) => ({ value }) => {
+   const formState = kasko.getFormState();
    ...
 }
```

## Example validators

### Simple usage

Simple validator that will show error on current input if emoji.

NOTE: Remember to respect `required` validator - it's ok to allow empty string in this example, because field with this validation rule might not be `required`.

In this example "a" and "a😀" will fail, but "" and "😀😁" will pass this validation rule.

```js
const ALL_EMOJI = /^[\uD800-\uDBFF\uDC00-\uDFFF]*$/;

window.registerKaskoPlugin('kasko-plugin', (kasko) => {
  return {
    validators: [
      // Adds validation rule `only_emoji_chars_allowed`
      {
        name: 'only_emoji_chars_allowed',
        validate(context) {
          // Show no error if field is not set
          if (!context.value) {
            return null;
          }

          // Detects if current input value contains only emoji chars
          const allCharsAreEmoji = ALL_EMOJI.test(context.value);

          if (!allCharsAreEmoji) {
            // Sets error to current input field
            return {
              only_emoji_chars_allowed: true,
            };
          }

          // Clears error for current input
          return null;
        },
      },
    ],
  };
});
```

### Complex usage

This is a more complex validator that takes in 2 arguments, one of them is a number and second is another field name. Then we check that field names value and detect if current input contains `n` number of chars that are in other field.

Valid case for this rule would be:

* Validation definition: "at\_least\_n\_chars\_present\_in:3,control\_field"
* Current field value: "ace"
* Form value: `{ control_field: 'abcde' }`

```js
window.registerKaskoPlugin('kasko-plugin', (kasko) => {
  return {
    validators: [
      /**
       * Adds validation rule `at_least_n_chars_present_in`
       *
       * @example usage in field definition:
       * `at_least_n_chars_present_in:3,control_field`
       *
       * It has 2 arguments `3` and `control_field`
       */
      {
        name: 'at_least_n_chars_present_in',
        validate(context) {
          // Show no error if field is not set
          if (!context.value) {
            return null;
          }

          // Accessing arguments from validation definition
          const [minChars, otherFieldName] = context.args;

          // Getting value for other field (note that other field might not be inside
          // current form, so we need to also check input state)
          const otherFieldValue = kasko.getFormState(otherFieldName) || kasko.getState(`input.${otherFieldName}`);

          // Creates a regexp for getting `n` chars from other field value
          const containsChars = new RegExp(`^[${otherFieldValue}]*$`).test(context.value);
          const hasMinChars = context.value.length >= parseInt(minChars, 10);

          // Checks with current input value
          if (!(containsChars && hasMinChars)) {
            // Sets error to current input field
            return {
              at_least_n_chars_present_in: true,
            };
          }

          // Clears error for current input
          return null;
        },
      },
    ],
  };
});
```
