{"id":16417082,"url":"https://github.com/tkrotoff/react-form-with-constraints","last_synced_at":"2025-06-29T10:37:49.393Z","repository":{"id":41390155,"uuid":"91469565","full_name":"tkrotoff/react-form-with-constraints","owner":"tkrotoff","description":"Simple form validation for React","archived":false,"fork":false,"pushed_at":"2023-01-27T13:55:49.000Z","size":5058,"stargazers_count":126,"open_issues_count":21,"forks_count":20,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-06-15T18:59:34.580Z","etag":null,"topics":["constraints","form","form-validation","html5","input-validation","react","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/tkrotoff.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}},"created_at":"2017-05-16T14:43:00.000Z","updated_at":"2024-08-23T17:48:53.000Z","dependencies_parsed_at":"2023-02-08T08:31:38.480Z","dependency_job_id":null,"html_url":"https://github.com/tkrotoff/react-form-with-constraints","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/tkrotoff/react-form-with-constraints","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkrotoff%2Freact-form-with-constraints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkrotoff%2Freact-form-with-constraints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkrotoff%2Freact-form-with-constraints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkrotoff%2Freact-form-with-constraints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkrotoff","download_url":"https://codeload.github.com/tkrotoff/react-form-with-constraints/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkrotoff%2Freact-form-with-constraints/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261452908,"owners_count":23160415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["constraints","form","form-validation","html5","input-validation","react","validation"],"created_at":"2024-10-11T07:10:58.401Z","updated_at":"2025-06-29T10:37:49.371Z","avatar_url":"https://github.com/tkrotoff.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-form-with-constraints\n\n[![npm version](https://badge.fury.io/js/react-form-with-constraints.svg)](https://badge.fury.io/js/react-form-with-constraints)\n[![Node.js CI](https://github.com/tkrotoff/react-form-with-constraints/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/tkrotoff/react-form-with-constraints/actions)\n[![codecov](https://codecov.io/gh/tkrotoff/react-form-with-constraints/branch/master/graph/badge.svg)](https://codecov.io/gh/tkrotoff/react-form-with-constraints)\n[![Bundle size](https://badgen.net/bundlephobia/minzip/react-form-with-constraints)](https://bundlephobia.com/result?p=react-form-with-constraints@latest)\n[![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n[![Airbnb Code Style](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)\n\nSimple form validation for React\n\n- Installation: `npm install react-form-with-constraints`\n- CDN: https://unpkg.com/react-form-with-constraints/dist/\n\nCheck the [changelog](CHANGELOG.md) for breaking changes and fixes between releases.\n\n## Introduction: what is HTML5 form validation?\n\n⚠️ [Client side validation is cosmetic, you should not rely on it to enforce security](https://stackoverflow.com/q/162159)\n\n```HTML\n\u003cform\u003e\n  \u003clabel for=\"email\"\u003eEmail:\u003c/label\u003e\n  \u003cinput type=\"email\" id=\"email\" required\u003e\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n![input required](doc/input-required.png)\n![input type=\"email\"](doc/input-type-email.png)\n\nThe `required` HTML5 attribute specifies that the user must fill in a value, [`type=\"email\"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email) checks that the entered text looks like an email address.\n\nResources:\n\n- [Making Forms Fabulous with HTML5](https://web.archive.org/web/20220317195146/https://www.html5rocks.com/en/tutorials/forms/html5forms/)\n- [Constraint Validation: Native Client Side Validation for Web Forms](https://web.archive.org/web/20210818094708/https://www.html5rocks.com/en/tutorials/forms/constraintvalidation/)\n- [web.dev - Learn Forms](https://web.dev/learn/forms/)\n- [MDN - Form data validation](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation)\n- [MDN - Form input types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_\u003cinput\u003e_types)\n- [UX Research Articles - Usability Testing of Inline Form Validation](https://baymard.com/blog/inline-form-validation)\n\n## What react-form-with-constraints brings\n\n- Minimal API and footprint\n- Unobtrusive: easy to adapt regular [React code](https://reactjs.org/docs/forms.html)\n- HTML5 error messages personalization: `\u003cFieldFeedback when=\"valueMissing\"\u003eMy custom error message\u003c/FieldFeedback\u003e`\n- Custom constraints: `\u003cFieldFeedback when={value =\u003e ...}\u003e`\n- Warnings and infos: `\u003cFieldFeedback ... warning\u003e`, `\u003cFieldFeedback ... info\u003e`\n- Async validation\n- No dependency beside React (no Redux, MobX...)\n- Re-render only what's necessary\n- Easily extendable\n- [Bootstrap](examples/Bootstrap) styling with npm package `react-form-with-constraints-bootstrap`\n- [Material-UI](examples/MaterialUI) integration with npm package `react-form-with-constraints-material-ui`\n- Support for [React Native](examples/ReactNative) with npm package `react-form-with-constraints-native`\n- ...\n\n```JSX\n\u003cinput type=\"password\" name=\"password\"\n       value={this.state.password} onChange={this.handleChange}\n       required pattern=\".{5,}\" /\u003e\n\u003cFieldFeedbacks for=\"password\"\u003e\n  \u003cFieldFeedback when=\"valueMissing\" /\u003e\n  \u003cFieldFeedback when=\"patternMismatch\"\u003e\n    Should be at least 5 characters long\n  \u003c/FieldFeedback\u003e\n  \u003cFieldFeedback when={value =\u003e !/\\d/.test(value)} warning\u003e\n    Should contain numbers\n  \u003c/FieldFeedback\u003e\n  \u003cFieldFeedback when={value =\u003e !/[a-z]/.test(value)} warning\u003e\n    Should contain small letters\n  \u003c/FieldFeedback\u003e\n  \u003cFieldFeedback when={value =\u003e !/[A-Z]/.test(value)} warning\u003e\n    Should contain capital letters\n  \u003c/FieldFeedback\u003e\n\u003c/FieldFeedbacks\u003e\n```\n\n## Examples\n\n- CodePen basic Password example: https://codepen.io/tkrotoff/pen/BRGdqL ([StackBlitz version](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/Password))\n\n  ![example-password](doc/example-password.png)\n\n- [Bootstrap example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/Bootstrap)\n- [Material-UI example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/MaterialUI)\n- [WizardForm example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/WizardForm)\n- [SignUp example (React classes)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/SignUp)\n- [ClubMembers example (React classes + MobX)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/ClubMembers)\n- [Password without state example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/PasswordWithoutState)\n- [Server-side rendering example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/ServerSideRendering)\n\n- [React Native example (React classes)](examples/ReactNative):\n\n  | iOS                                                   | Android                                                       |\n  | ----------------------------------------------------- | ------------------------------------------------------------- |\n  | ![react-native-example-ios](doc/react-native-ios.png) | ![react-native-example-android](doc/react-native-android.png) |\n\n- Other examples from [the examples directory](examples):\n  - [Plain old React form validation example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/PlainOldReact)\n  - [React with HTML5 constraint validation API example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/HTML5ConstraintValidationAPI)\n\n## How it works\n\nThe API works the same way as [React Router](https://reacttraining.com/react-router/web/example/basic):\n\n```JSX\n\u003cRouter\u003e\n  \u003cRoute exact path=\"/\" component={Home} /\u003e\n  \u003cRoute path=\"/news\" component={NewsFeed} /\u003e\n\u003c/Router\u003e\n```\n\nIt is also inspired by [AngularJS ngMessages](https://docs.angularjs.org/api/ngMessages#usage).\n\nIf you had to implement validation yourself, you would end up with [a global object that tracks errors for each field](examples/PlainOldReact/App.tsx).\nreact-form-with-constraints [works similarly](packages/react-form-with-constraints/src/FieldsStore.ts).\nIt uses [React context](https://reactjs.org/docs/legacy-context.html) to share the [`FieldsStore`](packages/react-form-with-constraints/src/FieldsStore.ts) object across [`FieldFeedbacks`](packages/react-form-with-constraints/src/FieldFeedbacks.tsx) and [`FieldFeedback`](packages/react-form-with-constraints/src/FieldFeedback.tsx).\n\n## API\n\nThe API reads like this: \"for field when constraint violation display feedback\", example:\n\n```JSX\n\u003cFieldFeedbacks for=\"password\"\u003e\n  \u003cFieldFeedback when=\"valueMissing\" /\u003e\n  \u003cFieldFeedback when=\"patternMismatch\"\u003eShould be at least 5 characters long\u003c/FieldFeedback\u003e\n\u003c/FieldFeedbacks\u003e\n```\n\n```\nfor field \"password\"\n  when constraint violation \"valueMissing\"    display \u003cthe HTML5 error message (*)\u003e\n  when constraint violation \"patternMismatch\" display \"Should be at least 5 characters long\"\n```\n\n(\\*) [element.validationMessage](https://www.w3.org/TR/html51/sec-forms.html#the-constraint-validation-api)\n\nAsync support works as follow:\n\n```JSX\n\u003cFieldFeedbacks for=\"username\"\u003e\n  \u003cAsync\n    promise={checkUsernameAvailability} /* Function that returns a promise */\n    then={available =\u003e available ?\n      \u003cFieldFeedback key=\"1\" info style={{color: 'green'}}\u003eUsername available\u003c/FieldFeedback\u003e :\n      \u003cFieldFeedback key=\"2\"\u003eUsername already taken, choose another\u003c/FieldFeedback\u003e\n      // Why key=*? Needed otherwise React gets buggy when the user rapidly changes the field\n    }\n  /\u003e\n\u003c/FieldFeedbacks\u003e\n```\n\nTrigger validation:\n\n```JSX\nfunction MyForm() {\n  const form = useRef(null);\n\n  async function handleChange({ target }) {\n    // Validates only the given fields and returns Promise\u003cField[]\u003e\n    await form.current.validateFields(target);\n  }\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n\n    // Validates the non-dirty fields and returns Promise\u003cField[]\u003e\n    await form.current.validateForm();\n\n    if (form.current.isValid()) console.log('The form is valid');\n    else console.log('The form is invalid');\n  }\n\n  return (\n    \u003cFormWithConstraints ref={form} onSubmit={handleSubmit} noValidate\u003e\n      \u003cinput\n        name=\"username\"\n        onChange={handleChange}\n        required minLength={3}\n      /\u003e\n      \u003cFieldFeedbacks for=\"username\"\u003e\n        \u003cFieldFeedback when=\"tooShort\"\u003eToo short\u003c/FieldFeedback\u003e\n        \u003cAsync\n          promise={checkUsernameAvailability}\n          then={available =\u003e available ?\n            \u003cFieldFeedback key=\"1\" info style={{color: 'green'}}\u003eUsername available\u003c/FieldFeedback\u003e :\n            \u003cFieldFeedback key=\"2\"\u003eUsername already taken, choose another\u003c/FieldFeedback\u003e\n          }\n        /\u003e\n        \u003cFieldFeedback when=\"*\" /\u003e\n      \u003c/FieldFeedbacks\u003e\n    \u003c/FormWithConstraints\u003e\n  );\n}\n```\n\n\u003cbr\u003e\n\n**Important note:**\n\nIf a field (i.e an `\u003cinput\u003e`) does not have a matching `FieldFeedbacks`, the library won't known about this field (and thus won't perform validation).\nThe field name should match `FieldFeedbacks.for`:\n\n```JSX\n\u003cinput name=\"MY_FIELD\" ...\u003e\n\u003cFieldFeedbacks for=\"MY_FIELD\"\u003e\n  ...\n\u003c/FieldFeedbacks\u003e\n```\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n- [`FieldFeedbacks`](packages/react-form-with-constraints/src/FieldFeedbacks.tsx)\n\n  - `for: string` =\u003e reference to a `name` attribute (e.g `\u003cinput name=\"username\"\u003e`), should be unique to the current form\n  - `stop?: 'first' | 'first-error' | 'first-warning' | 'first-info' | 'no'` =\u003e\n    when to stop rendering `FieldFeedback`s, by default stops at the first error encountered (`FieldFeedback`s order matters)\n\n  Note: you can place `FieldFeedbacks` anywhere, have as many as you want for the same `field`, nest them, mix them with `FieldFeedback`... Example:\n\n  ```JSX\n  \u003cinput name=\"username\" ... /\u003e\n\n  \u003cFieldFeedbacks for=\"username\" stop=\"first-warning\"\u003e\n    \u003cFieldFeedbacks\u003e\n      \u003cFieldFeedback ... /\u003e\n      \u003cAsync ... /\u003e\n      \u003cFieldFeedbacks stop=\"first-info\"\u003e\n        ...\n      \u003c/FieldFeedbacks\u003e\n    \u003c/FieldFeedbacks\u003e\n\n    \u003cFieldFeedback ... /\u003e\n    \u003cAsync ... /\u003e\n  \u003c/FieldFeedbacks\u003e\n\n  \u003cFieldFeedbacks for=\"username\" stop=\"no\"\u003e\n    ...\n  \u003c/FieldFeedbacks\u003e\n  ```\n\n- [`FieldFeedback`](packages/react-form-with-constraints/src/FieldFeedback.tsx)\n\n  - `when?`:\n    - [`ValidityState`](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) as a string =\u003e HTML5 constraint violation name\n    - `'*'` =\u003e matches any HTML5 constraint violation\n    - `'valid'` =\u003e displays the feedback only if the field is valid\n    - `(value: string) =\u003e boolean` =\u003e custom constraint\n  - `error?: boolean` =\u003e treats the feedback as an error (default)\n  - `warning?: boolean` =\u003e treats the feedback as a warning\n  - `info?: boolean` =\u003e treats the feedback as an info\n  - `children` =\u003e what to display when the constraint matches; if missing, displays the [HTML5 error message](https://www.w3.org/TR/html51/sec-forms.html#the-constraint-validation-api) if any\n\n- [`Async\u003cT\u003e`](packages/react-form-with-constraints/src/Async.tsx) =\u003e Async version of `FieldFeedback` (similar API as [react-promise](https://github.com/capaj/react-promise))\n\n  - `promise: (value: string) =\u003e Promise\u003cT\u003e` =\u003e a promise you want to wait for\n  - `pending?: React.ReactNode` =\u003e runs when promise is pending\n  - `then?: (value: T) =\u003e React.ReactNode` =\u003e runs when promise is resolved\n  - `catch?: (reason: any) =\u003e React.ReactNode` =\u003e runs when promise is rejected\n\n- [`FormWithConstraints`](packages/react-form-with-constraints/src/FormWithConstraints.tsx)\n\n  - `validateFields(...inputsOrNames: Array\u003cInput | string\u003e): Promise\u003cField[]\u003e` =\u003e\n    Should be called when a `field` changes, will re-render the proper `FieldFeedback`s (and update the internal `FieldsStore`).\n    Without arguments, all fields (`$('[name]')`) are validated.\n\n  - `validateFieldsWithoutFeedback(...inputsOrNames: Array\u003cInput | string\u003e): Promise\u003cField[]\u003e` =\u003e\n    Validates only all non-dirty fields (won't re-validate fields that have been already validated with `validateFields()`),\n    If you want to force re-validate all fields, use `validateFields()`.\n    Might be renamed to `validateNonDirtyFieldsOnly()` or `validateFieldsNotDirtyOnly()` in the future?\n\n  - `validateForm(): Promise\u003cField[]\u003e` =\u003e\n    Same as `validateFieldsWithoutFeedback()` without arguments, typically called before to submit the `form`.\n    Might be removed in the future?\n\n  - `isValid(): boolean` =\u003e should be called after `validateFields()`, `validateFieldsWithoutFeedback()` or `validateForm()`, indicates if the fields are valid\n\n  - `hasFeedbacks(): boolean` =\u003e indicates if any of the fields have any kind of feedback\n\n  - `resetFields(...inputsOrNames: Array\u003cInput | string\u003e): Field[]` =\u003e\n    Resets the given fields and re-render the proper `FieldFeedback`s.\n    Without arguments, all fields (`$('[name]')`) are reset.\n\n  - [`Field`](packages/react-form-with-constraints/src/Field.ts) =\u003e\n    ```TypeScript\n    {\n      name: string;\n      validations: { // FieldFeedbackValidation[]\n        key: number;\n        type: 'error' | 'warning' | 'info' | 'whenValid';\n        show: boolean | undefined;\n      }[];\n      isValid: () =\u003e boolean\n    }\n    ```\n\n- [`Input`](packages/react-form-with-constraints/src/Input.tsx)\n\n  If you want to style `\u003cinput\u003e`, use `\u003cInput\u003e` instead: it will add classes `is-pending`, `has-errors`, `has-warnings`, `has-infos` and/or `is-valid` on `\u003cinput\u003e` when the field is validated.\n\n  Example: `\u003cInput name=\"username\" /\u003e` can generate `\u003cinput name=\"username\" class=\"has-errors has-warnings\"\u003e`\n\n  FYI `react-form-with-constraints-bootstrap` and `react-form-with-constraints-material-ui` already style the fields to match their respective frameworks.\n\n## Browser support\n\nreact-form-with-constraints needs [`ValidityState`](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) which is supported by all modern browsers and IE 11.\nIt also needs a polyfill such as [core-js](https://github.com/zloirock/core-js) to support IE 11, see [React JavaScript Environment Requirements](https://reactjs.org/docs/javascript-environment-requirements.html).\n\nYou can use HTML5 attributes like `type=\"email\"`, `required`, [`minlength`](https://caniuse.com/#feat=input-minlength)...\n\n```JSX\n\u003clabel htmlFor=\"email\"\u003eEmail\u003c/label\u003e\n\u003cinput type=\"email\" name=\"email\" id=\"email\"\n       value={this.state.email} onChange={this.handleChange}\n       required /\u003e\n\u003cFieldFeedbacks for=\"email\"\u003e\n  \u003cFieldFeedback when=\"*\" /\u003e\n\u003c/FieldFeedbacks\u003e\n```\n\n...and/or rely on `when` functions:\n\n```JSX\n\u003clabel htmlFor=\"email\"\u003eEmail\u003c/label\u003e\n\u003cinput name=\"email\" id=\"email\"\n       value={this.state.email} onChange={this.handleChange} /\u003e\n\u003cFieldFeedbacks for=\"email\"\u003e\n  \u003cFieldFeedback when={value =\u003e value.length === 0}\u003ePlease fill out this field.\u003c/FieldFeedback\u003e\n  \u003cFieldFeedback when={value =\u003e !/\\S+@\\S+/.test(value)}\u003eInvalid email address.\u003c/FieldFeedback\u003e\n\u003c/FieldFeedbacks\u003e\n```\n\nIn the last case you will have to manage translations yourself (see SignUp example).\n\n## Notes\n\n- A [`type=\"hidden\"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/hidden#Validation), [`readonly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) or [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-disabled) input won't trigger any HTML5 form constraint validation like [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-required), see https://codepen.io/tkrotoff/pen/gdjVNv\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkrotoff%2Freact-form-with-constraints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkrotoff%2Freact-form-with-constraints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkrotoff%2Freact-form-with-constraints/lists"}