{"id":21254646,"url":"https://github.com/jhadev/use-form","last_synced_at":"2026-04-25T18:31:49.989Z","repository":{"id":57121195,"uuid":"205507929","full_name":"jhadev/use-form","owner":"jhadev","description":"React hook to quickly create stateful form components.","archived":false,"fork":false,"pushed_at":"2019-09-03T02:55:40.000Z","size":18,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-10T17:42:55.582Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@jhadev/use-form","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/jhadev.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":"2019-08-31T06:51:29.000Z","updated_at":"2019-09-03T02:55:42.000Z","dependencies_parsed_at":"2022-08-24T04:41:08.088Z","dependency_job_id":null,"html_url":"https://github.com/jhadev/use-form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jhadev/use-form","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhadev%2Fuse-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhadev%2Fuse-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhadev%2Fuse-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhadev%2Fuse-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jhadev","download_url":"https://codeload.github.com/jhadev/use-form/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhadev%2Fuse-form/sbom","scorecard":{"id":518475,"data":{"date":"2025-08-11","repo":{"name":"github.com/jhadev/use-form","commit":"a8eaef00c9da9f9a49d0748c6d8a6f5f2c98cdbc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/8 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T02:22:27.584Z","repository_id":57121195,"created_at":"2025-08-20T02:22:27.585Z","updated_at":"2025-08-20T02:22:27.585Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32273213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T18:29:39.964Z","status":"ssl_error","status_checked_at":"2026-04-25T18:29:32.149Z","response_time":59,"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":[],"created_at":"2024-11-21T03:57:32.359Z","updated_at":"2026-04-25T18:31:49.966Z","avatar_url":"https://github.com/jhadev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React hook to generate simple forms.\n\n_in progress_\n\n**EXPERIMENTAL**\n\nThere may be bugs and this only suits certain use cases.\n\nnpm install or yarn add @jhadev/use-form\n\n**uses bootstrap by default** but can be customized.\n\n**0.0.4 - support for textarea (define textarea in the option object)**\n\n_Next version will support an entire form rather than only inputs._\n\n```\n{\n  textarea: { rows: 4, maxLength: 200 },\n  placeholder: 'this is a text area'\n}\n```\n\n## **usage**\n\nExamples:\n\n- [Map all of state to inputs with no options](#map-all-of-state-to-inputs-with-no-options)\n- [Map some of state into inputs](#map-some-of-state-into-inputs)\n- [Map all of state into inputs with options](#map-all-of-state-into-inputs-with-options)\n- [Map and filter state into inputs with options](#map-and-filter-state-into-inputs-with-options)\n\n---\n\nIn any functional component...\n\n```\nimport React from 'react';\nimport { useForm } from '@jhadev/use-form';\n\nconst SomeForm = props =\u003e {\n\n  // initial state is first argument to useForm, name of form is second argument.\n\n  const { formState, setFormState, onChange, mapInputs } = useForm({\n\n    // initialize state here\n\n  }, 'some-form');\n\n  ...\n}\n\nexport default SomeForm\n```\n\nInput changes are handled automatically.\n\nDefaults to bootstrap form-control class names for inputs if no options are specified. If no ids are specified it will create ids based on the form name and the key in state.\n\nIf useForm is used in multiple components, the form name must be unique to that component.\nThis ensures ids will not be duplicated, but will still be styleable because they won't be randomly generated.\n\nIf no name is specified as the second argument the name of the form will be set as 'default'.\nThis means if another form is not named, there will be duplicate ids. It is just a fallback.\n\n- _ex:_ If a key in state is named **age** and the form is named **form-one** then **id=\"form-one-age\"**\n\n- **setFormState behaves just like this.setState**\n\n```\n  ...\n\n  const { formState, setFormState, onChange, mapInputs } = useForm({\n    email: '',\n    password: '',\n    success: false,\n    error: null\n  }, 'sign-in-form');\n\n  ...\n\n  const signInUser = () =\u003e {\n    const { email, password } = formState\n\n    props.firebase.signInWithEmailAndPassword(email, password)\n      .then(response =\u003e {\n        setFormState({ email: '', password: '', success: true })\n        ....\n      }).catch(error =\u003e {\n        setFormState({ error, success: false })\n      })\n  }\n\n  ...\n```\n\n### **Map all of state to inputs with no options**\n\n- _onChange is optional because it is already handled, but it is still available if needed._\n\n**Example all of state no options**\n\n```\n  const { formState, setFormState, onChange, mapInputs } = useForm({\n    name: '',\n    password: ''\n  }, 'example-state-form');\n\n  const displayInputs = mapInputs(formState)()\n\n  ...\n  return (\n    ...\n    \u003cdiv className='form-group'\u003e{displayInputs}\u003c/div\u003e\n    ...\n  )\n```\n\n**HTML Output**\n\n```\n\u003cdiv class=\"form-group\"\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-state-form-name\"\n    type=\"text\"\n    placeholder=\"Name\"\n    name=\"name\"\n    value=\"\"/\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-state-form-password\"\n    type=\"password\"\n    placeholder=\"Password\"\n    name=\"password\"\n    value=\"\"/\u003e\n\u003c/div\u003e\n```\n\n### **Map some of state into inputs**\n\n**Filter state example**\n\n```\n  const { formState, setFormState, onChange, mapInputs } = useForm({\n    name: '',\n    password: '',\n    success: false,\n    error: null\n  }, 'example-filter-form');\n\n  // second optional argument of mapInputs is an array of certain state values as strings to create into inputs\n\n  const displayInputs = mapInputs(formState, ['name', 'password'])();\n\n  ...\n  return (\n    ...\n    \u003cdiv className='form-group'\u003e{displayInputs}\u003c/div\u003e\n    ...\n  )\n```\n\n**HTML Output**\n\n```\n\u003cdiv class=\"form-group\"\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-only-filter-name\"\n    type=\"text\"\n    placeholder=\"Name\"\n    name=\"name\"\n    value=\"\"/\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-only-filter-password\"\n    type=\"password\"\n    placeholder=\"Password\"\n    name=\"password\"\n    value=\"\"/\u003e\n\u003c/div\u003e\n```\n\n### **Map all of state into inputs with options**\n\nOptions are: **label, id, className, placeholder, type, textarea**\n\nThere is no need to define classNames for every options object if they are all the same.\nIf a className property exists for the first options object, it will be automatically added to the rest of the inputs.\nIf classNames exist for any of the following option objects, they will not be replaced.\n\nLabels are off by default.\nIf a label is specified for an input field, but no id is provided, an id will be created to match the label's htmlFor property.\n\nTo define a textarea create a property in the object called 'textarea', and define either an empty object inside it for no options, or define rows and/or maxLength. Default row size is 3.\n\n```\n{\n  textarea: { rows: 4, maxLength: 200 },\n  placeholder: 'this is a text area'\n}\n```\n\nOR\n\n```\n{\n  textarea: {},\n  placeholder: 'this is a text area'\n}\n```\n\n```\nconst types = [\n  'date',\n  'datetime-local',\n  'email',\n  'number',\n  'password',\n  'range',\n  'search',\n  'tel',\n  'time',\n  'url',\n  'week',\n  'month'\n]\n\n/*\n  If the key in state matches one of these types it will be used as the type for the input field.\n  If it does not match and no type is specified for that input field,\n  the type will default to 'text'.\n*/\n```\n\n**Example with options**\n\n```\n  const { formState, setFormState, onChange, mapInputs } = useForm({\n    name: '',\n    password: '',\n    age: '',\n  }, 'example-options-form');\n\n  // If an input does not need options, insert an empty object as the placeholder. The order matters.\n\n  const formOptions = [\n    {\n      label: 'Enter your name.',\n      placeholder: 'Your Name Here',\n      type: 'text',\n      id: 'name-field'\n    },\n    {},\n    { label: 'How old are you?', type: 'number' }\n  ];\n\n  const displayInputs = mapInputs(formState)(formOptions);\n\n  return (\n    ...\n    \u003cdiv className='form-group'\u003e{displayInputs}\u003c/div\u003e\n    ...\n  )\n```\n\n**HTML output**\n\n```\n\u003cdiv class=\"form-group\"\u003e\n  \u003clabel for=\"name-field\"\u003eEnter your name.\u003c/label\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"name-field\"\n    type=\"text\"\n    placeholder=\"Your Name Here\"\n    name=\"name\"\n    value=\"\"/\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-options-form-password\"\n    type=\"password\"\n    placeholder=\"Password\"\n    name=\"password\"\n    value=\"\"/\u003e\n  \u003clabel for=\"example-options-form-age\"\u003eHow old are you?\u003c/label\u003e\n  \u003cinput\n    class=\"form-control mb-3\"\n    id=\"example-options-form-age\"\n    type=\"number\"\n    placeholder=\"Age\"\n    name=\"age\"\n    value=\"\"/\u003e\n\u003c/div\u003e\n```\n\n### **Map and filter state into inputs with options**\n\n**Filter and options example**\n\n```\nconst { formState, setFormState, onChange, mapInputs } = useForm({\n    name: '',\n    password: '',\n    success: false\n  }, 'example-all-form');\n\n  /*\n    Order matters.\n    Options are label, id, className, placeholder, type.\n    If className property exists for the first item,\n    it will be automatically added to the rest of the inputs.\n    If classNames exist for any of the following objects,\n    they will not be replaced.\n  */\n\n  const formOptions = [\n    {\n      label: 'Enter your name.',\n      placeholder: 'Do you even have a name?',\n      type: 'text',\n      className: 'form-control my-2'\n    },\n    { id: 'password-field' }\n  ];\n\n  // mapInputs second argument is an array to filter the inputs that need to be created.\n\n  const displayInputs = mapInputs(formState, ['name', 'password'])(formOptions);\n\n  ...\n  return (\n    ...\n    \u003cdiv className='form-group'\u003e{displayInputs}\u003c/div\u003e\n    ...\n  )\n```\n\n**HTML Output**\n\n```\n\u003cdiv class=\"form-group\"\u003e\n  \u003clabel for=\"example-all-form-name\"\u003eEnter your name.\u003c/label\u003e\n  \u003cinput\n    class=\"form-control my-2\"\n    id=\"example-all-form-name\"\n    type=\"text\"\n    placeholder=\"Do you even have a name?\"\n    name=\"name\"\n    value=\"\"/\u003e\n  \u003cinput\n    class=\"form-control my-2\"\n    id=\"password-field\"\n    type=\"password\"\n    placeholder=\"Password\"\n    name=\"password\"\n    value=\"\"/\u003e\n\u003c/div\u003e\n```\n\n❤ if you read this far.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhadev%2Fuse-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjhadev%2Fuse-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhadev%2Fuse-form/lists"}