{"id":23895191,"url":"https://github.com/friendsofecmascript/validatory","last_synced_at":"2026-06-13T01:31:33.639Z","repository":{"id":143663498,"uuid":"112498473","full_name":"FriendsOfECMAScript/Validatory","owner":"FriendsOfECMAScript","description":"A minimal yet powerful form validation library written in modern JavaScript. Zero dependencies!","archived":false,"fork":false,"pushed_at":"2019-01-02T09:56:55.000Z","size":110,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-11-14T01:03:00.608Z","etag":null,"topics":["form","form-validation","javascript","library","validation"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FriendsOfECMAScript.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-29T16:18:58.000Z","updated_at":"2019-01-02T09:52:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"aa626d25-ce66-4e85-af1a-5c5fe6dec78d","html_url":"https://github.com/FriendsOfECMAScript/Validatory","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/FriendsOfECMAScript/Validatory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FriendsOfECMAScript%2FValidatory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FriendsOfECMAScript%2FValidatory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FriendsOfECMAScript%2FValidatory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FriendsOfECMAScript%2FValidatory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FriendsOfECMAScript","download_url":"https://codeload.github.com/FriendsOfECMAScript/Validatory/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FriendsOfECMAScript%2FValidatory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34269363,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["form","form-validation","javascript","library","validation"],"created_at":"2025-01-04T15:52:05.248Z","updated_at":"2026-06-13T01:31:33.632Z","avatar_url":"https://github.com/FriendsOfECMAScript.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Validatory\n\u003e A minimal yet powerful form validation library written in modern JavaScript. Zero dependencies!\n\n## Installation\n\nThe recommended and the most suitable way to install it is using *Yarn*:\n```bash\n$ yarn add validatory\n```\nor alternatively with *NPM*:\n```bash\n$ npm install --save validatory\n```\n\n## How does it work?\nThe library will validate your *form-elements* using by default a ***RegExp* pattern based validation**, setting the \ncorresponding `data-validation-state` attribute both to the *form-element* and the associated *form* after each \n*form-element*'s **input** and **change** events.\n\nThe *form-elements* will be the ones that are queried with the initialization's `formElementSelector` parameter, and the \n`data-validation-state` will be added by default to the *form-element*'s root node.\n\nThe *form-element* will have the following states:\n\n- **data-validation-state=\"not-validated\"** - This is the initial validation state.\n- **data-validation-state=\"not-filled\"** - If the element is marked as *required* but hasn't any value yet.\n- **data-validation-state=\"valid\"** - The element's value is valid.\n- **data-validation-state=\"not-valid\"** - If the element value hasn't passed the validation process. (Generic state)\n- **data-validation-state=\"not-valid-custom-state\"** - If the element value hasn't passed the validation process and the `custom-state` has been defined.\n\nThe *form* will have the following states:\n\n- **data-validation-state=\"not-validated\"** - This is the initial validation state.\n- **data-validation-state=\"not-valid\"** - If any of it's *form-elements* state is not valid.\n- **data-validation-state=\"valid\"** - If all of it's *form-elements* state is valid.\n\nFor example, after the validation has been triggered, a non-valid form element will have the following state:\n```html\n\u003cinput type=\"text\" value=\"aaa\" data-validate data-validate-email required data-validation-state=\"not-valid\"/\u003e\n```\n\n## Features\n\n* **Asynchronous validation**\n* Validate **required**\n* Validate values with **regular expressions**\n* **Built-in validation patterns**: 9 digit phone number, Email, ...\n* Write your own **custom validator**\n\n## Usage\nIn order to work with the library your DOM must, at least, have the following *data attributes* properly set.\n\n- **data-validate**\n- **required**\n- **data-validate-phone**\n- **data-validate-email**\n- **data-validate-any**\n- **data-validate-pattern=\"your-pattern\"**\n- **data-validation-state-reference-selector=\"your-selector\"**\n\n### DOM attributes\n\n#### form\nIf you want to validate a **\\\u003cform\u003e**, you must include the `data-validate` attribute. It's validation state will \ndepend on it's elements' validation states. \n```html\n\u003cform action=\"/\" data-validate\u003e\u003c/form\u003e\n```\n\n#### form-element\nIf you want to validate an **\\\u003cinput\u003e**, **\\\u003ctextarea\u003e**, **\\\u003cselect\u003e**, ... , you must as well include the \n`data-validate` attribute. It's validation will be triggered after each `input` and `change` events. \n```html\n\u003cinput type=\"text\" data-validate/\u003e\n```\n\n#### form-element - required\nBy setting the `required` attribute, the form element's value will be required, and it's state won't be valid until \nit's filled..\n```html\n\u003cinput type=\"text\" data-validate required/\u003e\n```\n\n#### form-element - built-in validators\nAs the validation process is by default based on *RegExp* patterns, the library will provide you the most common \nvalue-type-associated patterns. For the time being, it will automatically add the corresponding `data-validate-pattern` \nattribute to the elements with the following `data-validate-`type attributes.\n\nBuilt-in validators won't work if the form element has not included the `data-validate` attribute.\n\n##### *data-validate-phone*\nIt will validate **9 digit phone numbers**. It will add the corresponding `data-validate-pattern` attribute to the \nelements with the `data-validate-phone` attribute on runtime.\n```html\n\u003cinput type=\"tel\" data-validate data-validate-phone/\u003e\n```\n\nBoth *123-123-123* and *123123123* phones will be valid.\n\n##### *data-validate-email*\nIt will validate the element's value as an **email**, adding the correponding `data-validate-pattern` attribute to the \nelements with the `data-validate-email` attribute.\n```html\n\u003cinput type=\"email\" data-validate data-validate-email/\u003e\n``` \n\n##### *data-validate-any*\nThis built-in validator will validate **any value** as valid.\n```html\n\u003cinput type=\"name\" data-validate data-validate-any required/\u003e\n```\n\n#### form-element - custom pattern validation\nApart from the built-in validators, you may want to validate a form element using a custom *RegExp* pattern. By adding a \n`data-validate-pattern=\"(-your RegExp pattern-)\"` attribute to an element with the `data-validate` attribute, the \nlibrary will automatically use the provided pattern for validating the associated value. For example:\n\n###### Any value but *aValue*\nThis pattern will test as valid any value but the provided one. \n```html\n\u003cselect data-validate data-validate-pattern=\"^(?!.*$aValue).*$\"\u003e\u003c/select\u003e\n```\n\n###### 2-5 letter palindrome\nThis pattern will test as valid any value but the provided one. \n```html\n\u003ctextarea data-validate data-validate-pattern=\"\\b(\\w)?(\\w)\\w?\\2\\1\"\u003e\u003c/textarea\u003e\n```\n\nAnd so on.\n\n#### Custom *data-validation-state* dom node\nIf you want the associated `data-validation-state` attribute to be set o node different from the selector matching one, \nyou will want to use the `data-validation-state-reference-selector` attribute. The library will look up for the first \nselector-matching parent. For example:\n```html\n\u003clabel class=\"form-checkbox\" tabindex=\"0\"\u003e\n    \u003cinput type=\"checkbox\" class=\"form-checkbox__check\" id=\"terms\" name=\"terms\" \n        required data-validate data-validate-any data-validation-state-reference-selector=\".form-checkbox\"\u003e\n    \u003cspan class=\"form-checkbox__content\"\u003eI have read an accept the terms and conditions\u003c/span\u003e\n\u003c/label\u003e\n```\n\n### Initialization - basic\nIn order to properly initialize the library, we will call the library's `init` method, passing at least the following \nparameters.\n\n```js\nimport {init} from 'validatory';\n\ninit({\n  formSelector: 'form',\n  formElementSelector: 'input, select, textarea'\n});\n```\n\nBoth *formSelector* and *formElementSelector* can be any valid CSS selectors.\n\n### Initialization - with callbacks\n\nInitialization with callbacks.\n\n```js\nimport {init} from 'validatory';\n\nconst\n  formValidationStateChangedCallback = formValidatorInstance =\u003e {\n    const formState = formValidatorInstance.state;\n    // ...\n  },\n  formElementValidationStateChangedCallback = formElementValidatorInstance =\u003e {\n    const formElementState = formElementValidatorInstance.state;\n    // ...\n  };\n\ninit({\n  formSelector: 'form',\n  formElementSelector: 'input, select, textarea',\n  onFormValidationStateChanged: formValidationStateChangedCallback,\n  onFormElementValidationStateChanged: formElementValidationStateChangedCallback\n});\n```\n\n### Styling\nAs the library will set a **data-validation-state** attribute, we will style the element using this *data attribute*. \nFor example:\n```scss\n.form-input {\n  \u0026[data-validation-state=\"not-filled\"],\n  \u0026[data-validation-state=\"not-valid\"] {\n    border-color: $form-input-border-color-error;\n  }\n\n  \u0026[data-validation-state=\"valid\"] {\n    border-color: $form-input-border-color-valid;\n  }\n}\n```\n\n### Displaying error messages\nWe will use the **data-validation-state** attribute to show/hide the associated error messages. For example:\n```html\n\u003cdiv class=\"form-group-input\"\u003e\n    \u003cdiv class=\"form-group-input__label\"\u003e\n        \u003clabel class=\"form-label\" for=\"required-phone\"\u003e\u003cspan class=\"form-label__required\"\u003e*\u003c/span\u003ePhone\u003c/label\u003e\n    \u003c/div\u003e\n    \u003cinput id=\"required-phone\" name=\"required-phone\" type=\"tel\" class=\"form-input\" placeholder=\"Enter a 9 digit phone...\" \n        required data-validate data-validate-phone autocomplete=\"off\" tabindex=\"0\"\u003e\n    \u003cdiv class=\"form-group-input__errors\"\u003e\n        \u003cp class=\"form-error form-error--not-filled\"\u003eThis field is required.\u003c/p\u003e\n        \u003cp class=\"form-error form-error--not-valid\"\u003eEntered phone does not match the required pattern.\u003c/p\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n```scss\n.form-group-input__errors {\n  display: none;\n  position: relative;\n  z-index: -1;\n}\n\n[data-validation-state=\"not-valid\"] + .form-group-input__errors,\n[data-validation-state=\"not-filled\"] + .form-group-input__errors {\n    display: block;\n}\n\n[data-validation-state=\"not-valid\"] + .form-group-input__errors {\n  .form-error--not-valid {\n    animation: $form-group-errors-animation;\n    display: block;\n  }\n}\n\n[data-validation-state=\"not-filled\"] + .form-group-input__errors {\n  .form-error--not-filled {\n    animation: $form-group-errors-animation;\n    display: block;\n  }\n}\n```\n\n### Extending the library's Validators\nIn order to extend the library's validators, we will add a new **Validator** instance to the *ValidatorRegistry*, \npassing the required constructor parameters.\n\nYour custom validation implementation must return/resolve with an object shaped as follows:\n\n```js\n{\n  valid: boolean,\n  errorCode: string (optional)\n}\n```\n \nFor example:\n\n#### Synchronous validator\n```js\nimport {validatorRegistry, Validator} from 'validatory';\n\nconst customValidator = new Validator({\n  supports: node =\u003e node.classList.contains('form-textarea'),\n  isEmpty: node =\u003e node.value === '',\n  isValid: node =\u003e ({\n    valid: node.value === 'test'\n  }),\n});\n\nvalidatorRegistry.add(customValidator);\n```\n\n#### Asynchronous validator\n```js\nimport {validatorRegistry, Validator} from 'validatory';\n\nconst yourAsyncValidationPromise = new Promise(resolve =\u003e setTimeout(() =\u003e resolve(true), 3000));\n\nconst customValidator = new Validator({\n  supports: node =\u003e node.classList.contains('form-textarea'),\n  isEmpty: node =\u003e node.value === '',\n  isValid: node =\u003e new Promise(resolve =\u003e yourAsyncValidationPromise.then(result =\u003e {\n    const valid = result === 'valid'; // Your custom validation\n    \n    return resolve(valid ? {valid} : {valid, errorCode: 'your-custom-error-code'});\n  })),\n});\n\nvalidatorRegistry.add(customValidator);\n```\n\n### Extending the library's Validators - Async validation helper\n\nThe library provides a helper method `asyncValidation` that will wrap your async logic and reduce the boilerplate needed to write a custom async validator.\n\n```js\nimport {asyncValidation} from 'validatory';\n\nconst isValid = node =\u003e asyncValidation(fetch('https://jsonplaceholder.typicode.com/posts/1'), response =\u003e \n  node.value === 'bla bla' ? {valid: true} : {valid: false, errorCode: 'no-service'});\n\nconst customAsyncValidator = new Validator({\n  supports: node =\u003e node.id === 'asyn-custom-validation-field',\n  isEmpty: node =\u003e node.value === '',\n  isValid: node =\u003e isValid(node)\n});\n````\n\n### Extending the library's Validators - Full example with Styles \u0026 custom `data-states`\nIf your are writting a custom validator that needs to display a custom error message based on the validation logic, \nyou will need to implement the validator and add these styles to your app. \n\nIn this example, we are using the `es6-promise-debounce` for debouncing the validation process.\n\n```js\nimport debounce from 'es6-promise-debounce';\nimport {validatorRegistry, Validator, asyncValidation} from 'validatory';\n\nconst\n  debouncedValidation = debounce(node =\u003e {\n    console.log('Asynchronous validation started');\n\n    const validZipCode = /^\\d{5}$/.test(node.value); // zip code format validation\n\n    if (!validZipCode) {\n      return {valid: false, errorCode: 'zip-code'};\n    }\n\n    return asyncValidation(fetch('https://jsonplaceholder.typicode.com/posts/1'), response =\u003e {\n      const valid = node.value === '01005';\n\n      return valid ? {valid} : {valid: false, errorCode: 'no-service'};\n    });\n  }, 500),\n  asyncValidator = new Validator({\n    supports: node =\u003e node.id === 'async',\n    isEmpty: node =\u003e node.value === '',\n    isValid: node =\u003e debouncedValidation(node),\n  });\n\nvalidatorRegistry.add(asyncValidator);\n``` \n\n```scss\n@import './../_definitions/animations';\n$form-group-errors-animation: $animation-vertical-node-in;\n\n[data-validation-state=\"not-valid-zip-code\"] ~ .form-group-input__errors .form-error--not-valid-zip-code,\n[data-validation-state=\"not-valid-no-service\"] ~ .form-group-input__errors .form-error--not-valid-no-service {\n  animation: $form-group-errors-animation;\n  display: block;\n}\n```\n\nFor a full working example, checkout the [tests/app][1] demo.\n\n[1]: https://github.com/FriendsOfECMAScript/Validatory/tree/master/tests/app\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffriendsofecmascript%2Fvalidatory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffriendsofecmascript%2Fvalidatory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffriendsofecmascript%2Fvalidatory/lists"}