{"id":18353647,"url":"https://github.com/taystack/create-form","last_synced_at":"2025-07-27T23:34:15.667Z","repository":{"id":150576260,"uuid":"607337879","full_name":"taystack/create-form","owner":"taystack","description":"React form library with very few opinions about your markup.","archived":false,"fork":false,"pushed_at":"2023-02-27T21:23:51.000Z","size":237,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T00:55:21.931Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taystack.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-27T19:38:15.000Z","updated_at":"2023-03-04T03:23:58.000Z","dependencies_parsed_at":"2023-04-26T17:16:17.417Z","dependency_job_id":null,"html_url":"https://github.com/taystack/create-form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/taystack/create-form","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taystack%2Fcreate-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taystack%2Fcreate-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taystack%2Fcreate-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taystack%2Fcreate-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taystack","download_url":"https://codeload.github.com/taystack/create-form/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taystack%2Fcreate-form/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267441361,"owners_count":24087772,"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","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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-05T21:42:44.210Z","updated_at":"2025-07-27T23:34:15.644Z","avatar_url":"https://github.com/taystack.png","language":"TypeScript","readme":"# create-form\n[![CircleCI](https://circleci.com/gh/taystack/create-form.svg?style=shield)](https://circleci.com/gh/taystack/create-form)\n\n[![npm downloads](https://img.shields.io/npm/dm/create-form.svg?style=flat-square)](https://www.npmjs.com/package/taystack/create-form)\n[![gzip size](https://img.badgesize.io/https://unpkg.com/taystack/create-form?compression=gzip\u0026amp;style=flat-square)](https://unpkg.com/taystack/create-form)\n[![npm version](https://img.shields.io/npm/v/taystack/create-forms.svg?style=flat-square)](https://www.npmjs.com/package/taystack/create-form)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\ncreate-form is a declarative, typescript-driven form control library. create-form allows you to quickly apply setters/getters/validators within atomic, props-less components by following React's Context API.\n\nThis README was generated by [anansi](https://github.com/ntucker/anansi/tree/master/packages/generator-js#readme).\n\n## Installation\n```bash\nyarn add @taystack/create-form\n```\n\n## Usage\n\nThis library employs the factory pattern when creating the data-layer for your form controls.\nIf you are familiar with `React.createContext` then you will be familiar with how to use this library.\n\n### `const MyForm = createForm(options? = {})`\n```typescript\nimport { createForm } from '@taystack/create-form';\n\ntype Login = {\n  email: string;\n  password: string;\n  session: boolean;\n}\n\nconst LoginForm = createForm\u003cLogin\u003e()\n// { Context, Provider }\n```\n\n### Options\n#### `options.validate: Record\u003cK = keyof T, (value: T[K], values: T) =\u003e string | undefined\u003e`\nProvide custom validation methods upon receiving changes to key `K` of provided type `T`.\nThe keys allowed in your `validate` option must match the keys you provided from type `T`,\nbut are not required.\n\nFor the `LoginForm` example, we can provide options to validate `email | password`:\n```typescript\nconst options = {\n  validate: {\n    email: (value) =\u003e !value.length ? 'Email cannot be empty' : undefined,\n    password: (value) =\u003e !value.length ? 'Password cannot be empty' : undefined,\n  },\n};\n```\n\n### `LoginForm.Provider`\nThis behaves like `React.createContext(...).Provider` such that access to `LoginForm` values can only be made from components within this provider.\n```tsx\nconst LOGIN_DEFAULTS: Login = {\n  email: '',\n  password: '',\n  session: false,\n}\nfunction LoginFormPage() {\n  return (\n    \u003cLoginForm.Provider defaultValue={LOGIN_DEFAULTS}\u003e\n      ...\n    \u003c/LoginForm.Provider\u003e\n  )\n}\n```\n\n#### Provider prop `defaultValue: T`\nWhere `T extends Record\u003cstring, string | number | boolean\u003e`. This library currently supports primitive values. `HTML` change events typically involve primitive values.\n\nThis will be the default values we use in our form as type `T`. In the example above, `T` is of type `Login`.\n\n### `LoginForm.Context`\nThis behaves like `React.createContext(...)` such that access to the `LoginForm` APIs can be made.\n```tsx\n// Controlled inputs\nfunction LoginFormEmailComponent() {\n  const form = React.useContext(LoginForm.Context);\n  return (\n    \u003c\u003e\n      \u003clabel htmlFor={form.email.name}\u003eEmail\u003c/label\u003e\n      \u003cinput\n        id={form.email.name}\n        name={form.email.name}\n        value={form.email.value}\n        onChange={(event) =\u003e form.set.email(event.target.value)}\n      /\u003e\n      {form.email.error \u0026\u0026 \u003cdiv\u003e{form.email.error}\u003c/div\u003e}\n    \u003c/\u003e\n  )\n};\n```\n\n### `const form = useContext(MyForm.Context)`\n|**attribute**|**type**|**effect**|\n|---|---|---|\n|`form.resetAll()`|`() =\u003e void`|Resets `form[FIELD]` values, errors, files, etc. Resets `form[FIELD].current`to the `defaultValue` for all fields.|\n|`form.toJSON()`|`() =\u003e void`|Returns JSON format matching shape `T`|\n|`form.toFormData()`|`() =\u003e FormData`|Obtain `FormData` for use with http request/fetch `Content-Type: application/x-www-form-urlencoded`.|\n|`form.toURLSearchParams()`|`() =\u003e string`|Returns query string with url-encoded form fields|\n\n\n### `const field = useContext(MyForm.Context)[FIELD]`\nEach field defined in `type T` is accessible via `form[FIELD]`\n|**attribute**|**type**|**effect**|\n|---|---|---|\n|`[FIELD].default`|`T[keyof T]`|Default value provided by you within `\u003cMyForm.Provider defaultValue={value} /\u003e`.|\n|`[FIELD].current`|`T`|Current state of the form value.|\n|`[FIELD].set(value: T[keyof T])`|`(value: T[keyof T]) =\u003e void`|Allows setting of `[FIELD].current` values for matching `[FIELD]`.|\n|`[FIELD].error`|`string \\| undefined`|Custom string will exist *if* you have the matching `options.validate[FIELD](value: T[keyof T])` defined.|\n|`[FIELD].name`|`string`|Provides a random, unique value for use as a primary key for any given field.|\n|`[FIELD].reset()`|`() =\u003e void`|Reset `[FIELD].current` state to `Provider.props.defaultValue[FIELD]`. Clears any files, reader data, etc.|\n|`[FIELD].handleFileEvent(event: React.ChangeEvent\u003cHTMLInputElement\u003e)`|`() =\u003e void`|Allow files to be referenced for a file change event. `onChange={handleFileEvent}`. *(see side effects)|\n\n#### Side effects\n\n##### `[FIELD].handleFileEvent(event: React.ChangeEvent\u003cHTMLInputElement\u003e)`\nThis particular function will capture the `event.target.files` for use outside of the component in question.\n\n## Example\nBasic login form with `POST fetch` using JSON payload.\n```tsx\nconst LoginForm = createForm\u003cLogin\u003e({\n  validate: {\n    email: (value) =\u003e value.length === 0 ? 'Cannot be empty' : undefined,\n    password: (value) =\u003e value.length === 0 ? 'Cannot be empty' : undefined,\n  }\n})\n\nfunction Login() {\n  return (\n    \u003cLoginForm.Provider defaultValue={{ email: '', password: '', session: false }}\u003e\n      \u003cLoginEmailInput /\u003e\n      \u003cLoginPasswordInput /\u003e\n      \u003cLoginSessionInput /\u003e\n      \u003cLoginSubmitButton /\u003e\n    \u003c/LoginForm.Provider\u003e\n  )\n}\nconst LoginEmailInput = () =\u003e {\n  const { email } = useContext(LoginForm.Context)\n  return \u003cinput\n    type=\"email\"\n    value={email.current}\n    onChange={(e) =\u003e email.set(e.target.value)}\n  /\u003e\n}\nconst LoginPasswordInput = () =\u003e {\n  const { password } = useContext(LoginForm.Context)\n  return \u003cinput\n    type=\"password\"\n    value={password.current}\n    onChange={(e) =\u003e password.set(e.target.value)}\n  /\u003e\n}\nconst LoginSessionInput = () =\u003e {\n  const { session } = useContext(LoginForm.Context)\n  return \u003cinput\n    type=\"checkbox\"\n    checked={session.current}\n    onChange={(e) =\u003e session.set(e.target.checked)}\n  /\u003e\n}\nconst LoginSubmitButton = () =\u003e {\n  const form = useContext(LoginForm.Context)\n  const handleClick = useCallback(() =\u003e {\n    fetch('/login', {\n      method: 'POST',\n      body: JSON.stringify(form.toJSON())\n    })\n  }, [form])\n  return \u003cbutton onClick={handleClick}\u003eLog in\u003c/button\u003e\n}\n```\n\n\n## Development\n\nWith [Visual Studio Code](https://code.visualstudio.com), simply press `F5` to start the development server and browser.\n\n#### Run dev:\n\n```bash\nyarn start\n```\n\n#### Build prod:\n\n`Ctrl+shift+B` in [Visual Studio Code](https://code.visualstudio.com)\n\n```bash\nyarn build\n```\n\n#### Run prod: (after build)\n\n```bash\nyarn start:server\n```\n\n#### Analyze production bundle sizes:\n\n```bash\nyarn build:analyze\n```\n\n#### Run with [React Profiler](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html):\n\n```bash\nyarn build:profile\n```\n\n#### Check Packages for duplicates or circular dependencies:\n\n```bash\nyarn pkgcheck\n```\n\n\n#### Run with [Storybook](https://storybook.js.org/):\n\n```bash\nyarn storybook\n```\n\n\n#### Share demo on Stackblitz\n\nhttps://stackblitz.com/github/taystack/create-form\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaystack%2Fcreate-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaystack%2Fcreate-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaystack%2Fcreate-form/lists"}