{"id":13801380,"url":"https://github.com/kwladyka/form-validator-cljs","last_synced_at":"2026-02-21T13:01:34.867Z","repository":{"id":65350900,"uuid":"154028855","full_name":"kwladyka/form-validator-cljs","owner":"kwladyka","description":"ClojureScript library to validate forms","archived":false,"fork":false,"pushed_at":"2019-11-22T18:18:07.000Z","size":1722,"stargazers_count":55,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-11-22T05:03:52.135Z","etag":null,"topics":["clojurescript","form-validation","frontend","spec"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kwladyka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-21T16:29:16.000Z","updated_at":"2024-10-22T23:51:27.000Z","dependencies_parsed_at":"2023-01-19T06:05:12.659Z","dependency_job_id":null,"html_url":"https://github.com/kwladyka/form-validator-cljs","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kwladyka/form-validator-cljs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kwladyka%2Fform-validator-cljs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kwladyka%2Fform-validator-cljs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kwladyka%2Fform-validator-cljs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kwladyka%2Fform-validator-cljs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kwladyka","download_url":"https://codeload.github.com/kwladyka/form-validator-cljs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kwladyka%2Fform-validator-cljs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29681468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T12:30:22.644Z","status":"ssl_error","status_checked_at":"2026-02-21T12:29:55.402Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["clojurescript","form-validation","frontend","spec"],"created_at":"2024-08-04T00:01:22.194Z","updated_at":"2026-02-21T13:01:34.850Z","avatar_url":"https://github.com/kwladyka.png","language":"Clojure","funding_links":[],"categories":["Awesome ClojureScript"],"sub_categories":["Validation"],"readme":"![](https://github.com/kwladyka/form-validator-cljs/workflows/master%20tests/badge.svg)\n![](https://github.com/kwladyka/form-validator-cljs/workflows/doc%20-%3E%20gh-pages/badge.svg)\n![](https://github.com/kwladyka/form-validator-cljs/workflows/clojars/badge.svg)\n\n# form-validator\n\nClojureScript library to validate forms.\n\n## Rationale\n\n- Move repeatable code for form validation from app to library.\n- Validate by `spec` and `fn`.\n- Custom messages. Could be `\"foo\"`, `{:level :warn :msg \"foo\"}` or whatever.\n- Custom workflow. Let you choose when to show messages: `on-blur` / `on-change` / immediately after load page / ...\n- Easy and simple independent small solution. Compatible with `re-frame`, `fulcro` or whatever.\n- Work with different types of inputs, also custom ones like in material UI.\n- Base logic to make custom UI, but no UI included. No limitations.\n\nWhy? I need it myself. But I didn't find any library which satisfy me, so I wrote my own.\n\nRead my article [form validation](https://clojure.wladyka.eu/posts/form-validation/) to learn more rationales.\n\n## Tutorial and Demo\n\nDiscover it naturally by real code: https://kwladyka.github.io/form-validator-cljs/\n\n\u003ca href=\"https://kwladyka.github.io/form-validator-cljs/\"\u003e\u003cimg src=\"https://storage.googleapis.com/kwladyka/form-validator-cljs/demo.gif\"\u003e\u003c/a\u003e\n\nPlease keep in mind it is an example. It could easy take actions on different events `on-change` / `on-blur` / button click. All is your choice.\n\n## Add dependency\n\n[![Clojars Project](https://img.shields.io/clojars/v/kwladyka/form-validator-cljs.svg)](https://clojars.org/kwladyka/form-validator-cljs)\n\n### Require in ns\n\n```clojure\n(:require [form-validator.core :as form-validator])\n```\n\n### Only if you use reagent\n\nTo be compatible with reagent library, needs to use `reagent.core/atom` instead `clojure.core/atom`.\n\n```clojure\n(ns app.core\n  (:require [reagent.core :as r]\n            [form-validator.core :as form-validator]))\n\n;; First line in core ns or dedicated init fn is a right place\n(swap! form-validator/conf #(merge % {:atom r/atom}))\n```\n\n## TL;DR\n\nInit form\n\n```clojure\n(-\u003e {:names-\u003evalue {:email \"\"\n                    :password \"\"}\n     :form-spec ::spec/form\n    (form-validator/init-form))\n```\n\nreturn `atom` contained map:\n\n```clojure\n{:form-spec :app.spec/form\n :names-\u003evalue {:email \"\" :password \"\"}\n :names-\u003einvalid {:email [:app.spec/form :app.spec/email] :password [:app.spec/form :app.spec/password :app.spec/password-not-empty]}\n :names-\u003eshow #{}\n :names-\u003evalidators {}}\n ```\n\nThen you can use functions from ns `form-validator.core`:\n\n- `event-\u003enames-\u003evalue!` - With `on-change` / `on-blur` input event to update values.\n- `event-\u003eshow-message` - With `on-blur` / `on-change` input event to trigger when show messages in UI.\n- `?show-message` - Get message to show in UI for input. Also to know if mark input as not valid in UI.\n- `form-valid?` - true / false\n- `validate-form-and-show?` - Call `validate-form` and show all messages. Use with submit button.\n\n## Specification\n\n### Init form\n\n```clojure\n\n;; clojure.spec.alpha\n\n(s/def ::email (s/and string? (partial re-matches #\"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,63}$\")))\n\n(s/def ::password-not-empty not-empty)\n(s/def ::password-length #(\u003c= 6 (count %)))\n(s/def ::password (s/and string? ::password-not-empty ::password-length))\n\n(s/def ::form (s/keys :req-un [::email ::password]\n                      :opt-un [::password-repeat]))\n\n;; form-valiadtor\n\n(-\u003e {:names-\u003evalue {:email \"\"\n                    :password \"\"}\n     :form-spec ::form\n     :names-\u003evalidators {:email [email-exist?]\n                         :password-repeat [password-repeat? ::spec-key]}}\n    (form-validator/init-form))\n```\n\n- `:names-\u003evalue` - Form inputs with values to initialize.  \nUse cases: Empty values for new data form / filled values with already existed data (update form) / if input is not required by spec `:opt-un` it can be ommited.\n- `:form-spec` - Spec to validate whole form.  \nShould use always, unless you don't have specs.\n- `:names-\u003evalidators` - Vector of spec keywords and fn. Order matter.  \nUse cases: When don't have `spec` for form / if checkbox \"accept terms\" is checked / `fn` to compare password-repeat / check if user already exist by API during registration.\n- `:names-\u003eshow` - `#{}` with names of inputs to show error messages on start.\nUse cases: Form with already filled values. \n\nYou can use `:form-spec` and `:names-\u003evalidators` together. `:form-spec` is checked first.\n\n### Interact with form\n\n`(init-form ...)` return `atom`:\n\n```clojure\n{:form-spec :app.spec/form\n :names-\u003evalue {:email \"\", :password \"\"}\n :names-\u003einvalid {:email [::form ::form-map ::email]\n                  :password [::form ::form-map ::password ::password-not-empty]}\n :names-\u003eshow #{}\n :names-\u003evalidators {:email #object[cljs$core$sp1], :password-repeat #object[cljs$core$sp1]}}\n ```\n\n- `:form-spec` - Init form value without any change.\n- `:names-\u003evalue` - Values of the form.\n- `:names-\u003einvalid` - Invalid inputs with reasons of validation fail.\n- `:names-\u003eshow` - Add name of the input here, when you want to show message in UI.\n- `:names-\u003evalidators` - All validators converted to one fn which works similar to `some`. Check all validators for specific input one by one, unless fail or return `nil`.\n\n### Messages\n\n- `:names-\u003evalidators` can contain `::spec-key` and `fn`.\n- Spec always return vector of `:cljs.spec.alpha/problems` `:via`. For example `[::form ::form-map ::password ::password-not-empty]`. It means spec `::form` refer to spec `::form-map`, which refer to spec `::password`, which refer to spec `::password-not-empty`, which failed.\n- `fn` can return vectors like spec, but also strings, map or any value.\n```clojure\n;; Check error for input name \"password\"\n(-\u003e\u003e {::email \"Typo? It doesn't look valid.\"\n      ::password \"Minimum 6 characters and one special character !@#$%^\u0026*.\"\n      :password-not-equal \"Repeat password has to be the same.\"}\n     (form-validator/?show-message form :password))\n```\nBased on `[::form ::form-map ::password ::password-not-empty]` it is trying to find `::password-not-empty` message. Map not contain message for this spec. Then try to find `::password` and return message. If not find, going deeper. If not find any, return `true`.\n\nIf reason of fail is not a vector, then it is returning as it is. For example `\"cutom message\"` or `{:level :warn :msg \"This is only warning.\"}`. This is dedicated for `fn` validators.\n\n## Tips \u0026 Tricks \u0026 FAQ\n\n- Architecture of library let you make custom UI and validation on it. You can modify `atom` returned by `form-init`, `add-watch` on `atom`, add functions on top of core functions, use your own functions instead of core ones. It is designed to let you make custom things. In most of cases you really don't need to do it. It could be useful if you want to make your module based on this one.\n- To not prevent send form with warning (not error) messages like \"Password is weak. We recommend to use better password\" you have to use your own `form-valid?` function or make two `form-init` (first for errors and second for warnings). I decided to not make it as part of this library, because it is individual thing for project.\n- You want to write your own functions to generate UI HTML form and inputs based on this library. UI is individual thing for project, so I decided it wouldn't be part of this library. Instead this library give solid basement, which let you to build visualisation on it.\n\n---\n\nEverything below this line is mainly for myself as a maintainer of this library.\n\n## Developing\n\nLibrary has to be always check with web browsers manually! Not only automated tests. The reasons are differences between web browsers and practical aspects of usability vs imagination :)\n\nTo do it use `doc` branch from this repository.\n\nAfter all make a commit to readme with new sha hash for deps.edn.\n\n### Tests\n\n`clj -A:test:test-once`\n\n`clj -A:test:test-watch`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkwladyka%2Fform-validator-cljs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkwladyka%2Fform-validator-cljs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkwladyka%2Fform-validator-cljs/lists"}