{"id":24187782,"url":"https://github.com/emilmalanczak/hookform-input","last_synced_at":"2026-02-13T01:02:07.720Z","repository":{"id":262904142,"uuid":"887312234","full_name":"EmilMalanczak/hookform-input","owner":"EmilMalanczak","description":"Smart, polymorphic and typesafe Input component for react-hook-form","archived":false,"fork":false,"pushed_at":"2024-11-14T22:50:40.000Z","size":115,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-15T06:11:01.990Z","etag":null,"topics":["generics","polymorphic","react","react-hook-form","utility"],"latest_commit_sha":null,"homepage":"https://hookform-input-docs.vercel.app/","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/EmilMalanczak.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":"2024-11-12T14:26:39.000Z","updated_at":"2024-11-14T22:50:44.000Z","dependencies_parsed_at":"2024-11-14T23:31:12.260Z","dependency_job_id":"754cc0f1-1836-4160-8086-d35fbc0be571","html_url":"https://github.com/EmilMalanczak/hookform-input","commit_stats":null,"previous_names":["emilmalanczak/hookform-input"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmilMalanczak%2Fhookform-input","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmilMalanczak%2Fhookform-input/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmilMalanczak%2Fhookform-input/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmilMalanczak%2Fhookform-input/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EmilMalanczak","download_url":"https://codeload.github.com/EmilMalanczak/hookform-input/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233748648,"owners_count":18724101,"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":["generics","polymorphic","react","react-hook-form","utility"],"created_at":"2025-01-13T13:36:06.826Z","updated_at":"2025-09-21T11:32:28.043Z","avatar_url":"https://github.com/EmilMalanczak.png","language":"TypeScript","readme":"# Introduction\n\nTypesafe and simple implementation of polymorphic _Smart component_ component for `react-hook-form` package.\n\n## Motivation\n\nIf you are here I suppose you use `react-hook-form` to work with your forms. Have you heard about [Smart components](https://react-hook-form.com/advanced-usage#SmartFormComponent)? It's really cool approach - **_magic 🪄_** just happens.\n\nMain purpose of this project is to make this pattern easy to integrate with your current codebase by combining **_polymoprhic_** and **_Smart Component_** approaches.\n\n## Getting started\n\n```bash\nnpm install hookform-input\n\nyarn add hookform-input\n\npnpm add hookform-input\n```\n\n#### Bare minimum to start with!\n\n```tsx\nimport { FormInput } from 'hookform-input';\nimport { FormProvider, useForm } from 'react-hook-form';\n\nconst NameForm = () =\u003e {\n    const form = useForm();\n\n    const onSubmit = (values) =\u003e {\n        console.log(values);\n    };\n\n    return (\n        \u003cFormProvider {...formData}\u003e\n            \u003cform onSubmit={form.handleSubmit(onSubmit)}\u003e\n                \u003cFormInput name=\"username\" /\u003e\n\n                \u003cButton type=\"submit\"\u003eSubmit\u003c/Button\u003e\n            \u003c/form\u003e\n        \u003c/FormProvider\u003e\n    );\n};\n```\n\n## Core\n\nTo achieve maximum compatibility with UI libraries `FormInput` is making usage of **controlled** version of input via `useController` hook and/or\\* `Context API` exposed by `react-hook-form`.\n\nAs everything in apart of some pros there are also cons coming with each decision:\n\nPros:\n\n-   more flexible API\n-   you can use **_any_** component that supports `value` and `onChange` props (or their equivalents) as `input` prop.\n\nCons:\n\n-   using `FormProvider` might lead to potential [performance issues](https://www.react-hook-form.com/advanced-usage/#FormProviderPerformance) in more complex forms due to more re-renders\\*\n\n∗ _depends on what variant of `FormInput` you are using_\n\n### Smart \u0026 Polymorphic approach 🧠\n\n#### Polymorphic components\n\nIn a few words, it is a component that lets us specify which React element we want to use for its root. If you’ve used some UI library, such as [Mantine](https://mantine.dev/) or [Material UI](https://mui.com/material-ui/), you’ve already encountered a polymorphic component.\n\n#### Smart components\n\nAccording to `react-hook-form` docs:\n\n_\"This idea here is that you can easily compose your form with inputs.\"_\n\nHowever, it's not that easy to achieve in a real-world application. This package is here to help you with that.\nThe [example from the docs](https://react-hook-form.com/advanced-usage#SmartFormComponent) is working perfectly fine but has some limitations eg. doesn't supports nested component fields.\nAnother problem is lack of **typescript** support for `name` field which is really annoying.\n\n## API\n\n### `FormInputBare`\n\nThis is a **generic** component so you can provide your own type for `Form` and `input` component props.\nIn this variant the the `Form` type is being read base on the `control` prop so its not required to provide it.\n\nWe get full type safety for the `input` component props and our field `name`.\n\n```ts\n{\n  /**\n   * The component used for the root node. Either a string to use a HTML element or a component.\n   */\n  input?: Input;\n  /**\n        @string name of the field in form\n      */\n  name: Path\u003cForm\u003e;\n  /**\n       @object control object from useForm hook\n     */\n  control: Control\u003cForm\u003e;\n  /**\n      @string optional field from form fields to display error message\n    */\n  alternativeErrorKeys?: Path\u003cForm\u003e[];\n  /**\n      @boolean if true will log to console input changes with detailed information\n    */\n  debug?: boolean;\n  /**\n      @string in case your component uses different key than value eg. \"checked\" for checkbox\n      @default \"value\"\n    */\n  valueKey?: string;\n  /**\n      @string key to use for adapter\n    */\n    adapterKey?: keyof FormInputAdapterKeys;\n};\n```\n\n`FormInputAdapterKeys` is a global interface\n\nAnd additional props supported by the specified `input` component. Also if you want to pass some additional props directly to the `useController` hook each of its props is available with `_controller` prefix eg. `_controllerRules`.\n\n### `FormInput`\n\nBasically it's a re-export of `FormInputBare` with predefined `control` props by subscribing to the `FormProvider` context by `useFormContext` hook.\n\n`FormInput` share the same API as `FormInputBare` except the `control` prop which is omitted.\n\n### Input factory\n\nYou might not want to pass `input` prop manually all the time over and over again.\nEspecially when you work in larger projects with a lot of forms.\nProbably you have some kind of `Input` component that you use in your forms or that is coming from a UI library of your choice.\n\nThis is why we have a factory function that allows you to create a new component with predefined `input` prop.\n\n## Usage\n\n### Simple forms\n\n```tsx\nimport { FormInput, FormInputBare } from \"hookform-input\";\nimport { useForm } from \"react-hook-form\";\n\nconst NameForm = () =\u003e {\n  const form = useForm({\n    defaultValues: {\n      username: \"\",\n    },\n  });\n\n  const onSubmit = (values) =\u003e {\n    console.log(values);\n  };\n\n  return (\n    \u003cform onSubmit={form.handleSubmit(onSubmit)}\u003e\n      \u003cFormInputBare name=\"username\" control={form.control} /\u003e\n      \u003cButton type=\"submit\"\u003eSubmit\u003c/Button\u003e\n    \u003c/form\u003e\n    // OR\n    \u003cFormProvider {...form}\u003e\n      \u003cform onSubmit={form.handleSubmit(onSubmit)}\u003e\n        \u003cFormInput name=\"username\" /\u003e\n        \u003cButton type=\"submit\"\u003eSubmit\u003c/Button\u003e\n      \u003c/form\u003e\n    \u003c/FormProvider\u003e\n  );\n};\n```\n\n### Factory\n\n```tsx\ntype TestInputProps = {\n  value?: number;\n  onChange: (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e void;\n  // this will be still required\n  randomProp: string;\n};\n\nconst TestInput = ({ value, onChange, randomProp }: TestProps) =\u003e {\n  return \u003cinput value={value} onChange={onChange} /\u003e;\n};\n\nconst TestFormInput = createFormInput(TestInput)\nconst TestFormInputBare = createFormInput(TestInput, {\n    bare: true,\n    defaultProps: {\n        randomProp: \"default\"\n    }\n})\n\n// Usage\n\u003cTestFormInput\u003cTestForm\u003e\n  randomProp=\"text\"\n  name=\"nest\"\n/\u003e\n\n\u003cTestFormInputBare\n  randomProp=\"text\"\n  name=\"nest\"\n  control={form.control}\n/\u003e\n\n```\n\n### Global Adapters\n\nTo make integration with different UI libraries easier, you can use the `formInputAdapters` object to register custom input adapters.\nIt is simple implementation of Adapter Pattern that allows you to transform the props passed to the `hookform-input` component to match the expected format of the UI library or your actual components that you are using without unnessesary refactor or adjustments.\n\nFor example - `Mantine` is working without any extra work. Even things like displaying error message are handled correctly because it's `TextInput` component receives `error` as a string prop.\n\n`Material UI` on the other hand has slightly different - to display error message we have to pass `error` as a boolean **and** `helperText` string props, thats why _Adapters_ comes helpful.\n\n#### Usage with Material UI\n\n```tsx\nimport { formInputAdapters } from 'hookform-input';\n\ntype MuiTextFieldProps = Omit\u003cFormInputForwardedProps, 'error'\u003e \u0026 {\n    error?: boolean;\n    helperText?: string;\n};\n\ndeclare global {\n    interface FormInputAdapterKeys {\n        'MUI-TextField': MuiTextFieldProps;\n    }\n}\n\nformInputAdapters.register({\n    key: 'MUI-TextField',\n    transformFn: (props, originalProps) =\u003e ({\n        ...props,\n        error: !!props.error,\n        helperText: props.error ?? originalProps?.description,\n    }),\n});\n```\n\nNow, when you use the `hookform-input` with the `MUI-TextField` key, the props will be transformed to match the expected format.\n\n```tsx\nimport TextField from '@mui/material/TextField';\nimport { useForm } from 'react-hook-form';\n\nconst Component = () =\u003e {\n    const form = useForm({\n        defaultValues: {\n            text: 'some value',\n        },\n    });\n\n    const onSubmit = (data) =\u003e {\n        console.log(data);\n    };\n\n    return (\n        \u003cform onSubmit={format.handleSubmit(onSubmit)}\u003e\n            \u003cFormInputBare name=\"text\" input={TextField} adapterKey=\"MUI-TextField\" control={form.control} /\u003e\n\n            \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n        \u003c/form\u003e\n    );\n};\n```\n\nby making usage of factories we can get rid of repeating `input` and `adapterKey` props.\n\n```tsx\nimport TextField from '@mui/material/TextField';\n\nconst MaterialFormInput = createFormInput(TextField, {\n    bare: true,\n    defaultProps: {\n        adapterKey: 'MUI-TextField',\n    },\n});\n\n/* code */\n\nreturn (\n        \u003cform onSubmit={format.handleSubmit(onSubmit)}\u003e\n            \u003cMaterialFormInput name=\"text\" control={form.control} /\u003e\n            \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n        \u003c/form\u003e\n    );\n\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilmalanczak%2Fhookform-input","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femilmalanczak%2Fhookform-input","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilmalanczak%2Fhookform-input/lists"}