{"id":27938526,"url":"https://github.com/willowtreeapps/react-formable","last_synced_at":"2025-05-07T08:49:00.990Z","repository":{"id":37692878,"uuid":"42971819","full_name":"willowtreeapps/react-formable","owner":"willowtreeapps","description":"React Forms","archived":false,"fork":false,"pushed_at":"2023-07-11T07:25:22.000Z","size":2681,"stargazers_count":25,"open_issues_count":29,"forks_count":5,"subscribers_count":43,"default_branch":"develop","last_synced_at":"2025-04-21T16:52:43.398Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willowtreeapps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2015-09-23T01:49:30.000Z","updated_at":"2021-04-16T01:36:14.000Z","dependencies_parsed_at":"2024-06-21T16:34:46.404Z","dependency_job_id":"052e427b-3b2f-4f0d-bfed-1cabe96e630f","html_url":"https://github.com/willowtreeapps/react-formable","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willowtreeapps%2Freact-formable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willowtreeapps%2Freact-formable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willowtreeapps%2Freact-formable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willowtreeapps%2Freact-formable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willowtreeapps","download_url":"https://codeload.github.com/willowtreeapps/react-formable/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252681926,"owners_count":21787766,"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":[],"created_at":"2025-05-07T08:48:59.486Z","updated_at":"2025-05-07T08:49:00.983Z","avatar_url":"https://github.com/willowtreeapps.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-formable\n\nThe goal of `react-formable` is to provide a simple API to serialize and validate forms. Out of the box, it is compatible with vanilla HTML inputs along with a wide variety of custom inputs.\n\nWe do this by passing a `name` to each input element within our `Form`. The form then tracks all these named inputs, whenever one of them changes or the form is submitted, it optionally serializes and validates them and sends them to the `onChange` and `onSubmit` callbacks.\n\nFor validation, the `Form` component looks for any `validators` array which is a prop or static member of a component. This array can have functions which accept the input's current value and the entire form's current value, and returns any value. If the result is truthy, it is considered an error. If a promise is returned, any resolved or rejected truthy value is considered an error.\n\nThat covers the basics of using `react-formable`. There are plenty of props to customize the default behavior of the form including:\n\n* **Validating the form**: We provide three options for validating the form. By default we validate the form fully when the form is submitted. You can also optionally validate the entire form form, or just the dirty fields, when an input changes.\n* **Rendering error messages**: There are two primary ways you can communicate errors to the user. We provide an `\u003cErrors /\u003e` that when placed inside the form, it will automatically display any errors within the form. If you want to display errors on the field level, we provide a `mapFieldErrorsToProps` function which takes the field errors of the individual component, the props of that component, and the resulting object is merged into the props of the component. The default for this function appends an `error` class to every input with an associated error.\n* **Debouncing validation**: A common use case is to show errors on an individual component level after the user has stopped typing in that field. Between validating only the dirty fields, mapping field errors to props, and debouncing validation, we get this out of the box for free.\n* **Inline hits**: Sometimes you may want to display hints to the user as they are typing in a field. Validators can return anything, including an object with extra information. Furthermore, validators get a third parameter which is the source of the event, either `onChange`, `onSubmit`, or `onSerialize`. We can attach this tag to the return value of the validators and use this additional information to render hints or errors.\n\n## API\n\n```ts\ninterface Props {\n  className?: string\n  propName?: string // default \"name\": The prop which signifies to formable that this component should be tracked\n  showErrorsOnChange?: boolean | 'field' | 'form' // default false: If the form should show errors onChange. `true` maps to \"form\" which validates the whole form. \"field\" only validates dirty fields.\n  showErrorsOnSubmit?: boolean // default true:  Shows errors when the form is submited\n  onChange?: (fieldValues: FieldValues, validation?: validation) =\u003e void\n  onSubmit?: (fieldValues: FieldValues, validation: validation) =\u003e void\n  fieldErrorsToProps?: (errors: any[], props: any) =\u003e any\n  debounceValidation?: number // default 0: in miliseconds\n  configureForm?: ConfigureForm\n  removeValidators?: boolean // default true: removes `validators` from the props of tracked components\n  removePropName?: boolean // default false: removes `propName` from the props of tracked components\n}\n\ninterface FieldErrors {\n  [name: string]: any\n}\n\ninterface FieldValues {\n  [name: string]: any\n}\n\ninterface Validation {\n  valid: boolean\n  fieldErrors: {\n    [key: string]: any\n  }\n  errors: any[]\n}\n\ninterface ConfigureForm {}\n```\n\n## Basic Usage\n\nLets start things off with a simple example, a login form.\n\n```tsx\nimport * as React from 'react'\nimport { render } from 'react-dom'\nimport Form from 'react-formable'\n\n// Our hypothetical login action\nconst onSubmit = ({ username, password }) =\u003e login(username, password)\n\nconst LoginForm = props =\u003e (\n  \u003cForm onSubmit={onSubmit}\u003e\n    \u003cinput name=\"username\" type=\"text\" placeholder=\"username\" /\u003e\n    \u003cinput name=\"password\" type=\"password\" placeholder=\"password\" /\u003e\n    \u003cbutton type=\"submit\"\u003eLogin\u003c/button\u003e\n  \u003c/Form\u003e\n)\n\nrender(\u003cExample /\u003e, document.getElementById('example'))\n```\n\nNothing terribly exciting, let's break it down and see what is going on. First, we have the `Form` component exposed by `react-formable`. This wraps all of our inputs and other markup. It exposes two callbacks which we can tap into to grab the state of the form, `onSubmit` and `onChange`.\n\nMoving along, we have a few vanilla DOM nodes, two `input`s and a `button`. The `Form` component tracks all components with a `name` property. The `name` value is then used as the serialized key for the field value. Clicking on the `button` submits the form just as it would a normal `form` tag.\n\nLet's add a few some validation!\n\n```tsx\nimport * as React from 'react'\nimport { render } from 'react-dom'\nimport Form from 'react-formable'\nimport Errors from 'react-formable/Errors'\n\n// Our hypothetical login action\nconst onSubmit = ({ username, password }, validation) =\u003e {\n  if (validation.valid) {\n    login(username, password)\n  }\n}\n\n// Simulate a network request to check if the username is taken\nconst isUsernameTaken = value =\u003e\n  new Promise(resolve =\u003e\n    setTimeout(() =\u003e {\n      resolve(value === 'taken')\n    }, 1000)\n  )\n\nconst LoginForm = props =\u003e (\n  \u003cForm onSubmit={onSubmit}\u003e\n    \u003cinput\n      name=\"username\"\n      type=\"text\"\n      placeholder=\"username\"\n      validators={[\n        value =\u003e !value.length \u0026\u0026 'Username is required',\n        value =\u003e\n          isUsernameTaken(value).then(\n            isTaken =\u003e isTaken \u0026\u0026 'Username is already taken.'\n          ),\n      ]}\n    /\u003e\n\n    \u003cinput\n      name=\"password\"\n      type=\"password\"\n      placeholder=\"password\"\n      validators={[\n        value =\u003e !value.length \u0026\u0026 'Password required.',\n        value =\u003e\n          value.length \u003c 8 \u0026\u0026 'Password must be longer than 8 characters.',\n      ]}\n    /\u003e\n\n    \u003cbutton type=\"submit\"\u003eLogin\u003c/button\u003e\n\n    \u003cErrors /\u003e\n  \u003c/Form\u003e\n)\n\nrender(\u003cExample /\u003e, document.getElementById('example'))\n```\n\nWe added a few things to our login form! `Form` scans all named components for a prop called `validators` which is an array of functions. These functions are called with the value of the of the input along with all values of the form. If you return a truthy value, it is considered an error. If you return a promise, any truthy value either resolved or rejected from the promise is considered an error. Lastly, `Form` strips `validators` from the component by default letting you have validation with any input.\n\nBack to our example, the username field has two validators. One which just checks if the username has length. The second simulates a network request to see if the username provided is `\"taken\"`, if so, it returns and error message.\n\nWe also provide an `Errors` component which renders any errors in the form. We can optionally choose when to show errors, either `onChange` or `onSubmit` (by default).\n\n## Dev\n\n```\nyarn start\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillowtreeapps%2Freact-formable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillowtreeapps%2Freact-formable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillowtreeapps%2Freact-formable/lists"}