{"id":16720043,"url":"https://github.com/maxatwork/form2js","last_synced_at":"2026-04-01T20:49:44.087Z","repository":{"id":45920306,"uuid":"906794","full_name":"maxatwork/form2js","owner":"maxatwork","description":"Parse browser forms into structured JavaScript objects. Six adapters — React hooks, vanilla DOM, jQuery, FormData, and more. One coherent API. ","archived":false,"fork":false,"pushed_at":"2026-03-27T23:02:13.000Z","size":653,"stargazers_count":632,"open_issues_count":0,"forks_count":135,"subscribers_count":34,"default_branch":"master","last_synced_at":"2026-03-28T05:43:58.504Z","etag":null,"topics":["deserialization","form","form-data","forms","hook","html","javascript","jquery","jquery-plugin","json","object-transform","parser","react","serialization","server","submit"],"latest_commit_sha":null,"homepage":"https://maxatwork.github.io/form2js/","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/maxatwork.png","metadata":{"files":{"readme":"README.md","changelog":"changeset/config.json","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2010-09-13T09:49:47.000Z","updated_at":"2026-03-27T23:02:14.000Z","dependencies_parsed_at":"2026-02-18T19:01:46.553Z","dependency_job_id":null,"html_url":"https://github.com/maxatwork/form2js","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/maxatwork/form2js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxatwork%2Fform2js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxatwork%2Fform2js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxatwork%2Fform2js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxatwork%2Fform2js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxatwork","download_url":"https://codeload.github.com/maxatwork/form2js/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxatwork%2Fform2js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"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":["deserialization","form","form-data","forms","hook","html","javascript","jquery","jquery-plugin","json","object-transform","parser","react","serialization","server","submit"],"created_at":"2024-10-12T22:05:27.199Z","updated_at":"2026-04-01T20:49:44.073Z","avatar_url":"https://github.com/maxatwork.png","language":"TypeScript","funding_links":[],"categories":["Web Frontend"],"sub_categories":["React \u0026 UI Libraries"],"readme":"# form2js\n\n🚀 **form2js is back — modernized and actively maintained.**\n\nOriginally created in 2010, now rewritten for modern JavaScript, TypeScript, ESM, React, and modular usage.\n\nLegacy version is available in the [legacy branch](https://github.com/maxatwork/form2js/tree/legacy).\n\nMigrating from legacy form2js? Start with the [migration guide](https://maxatwork.github.io/form2js/migrate/).\n\n## Description\n\nA small family of packages for turning form-shaped data into objects, and objects back into forms.\n\nIt is not a serializer, not an ORM, and not a new religion. It just does this one job, does it reliably, and leaves before anyone starts a committee about it.\n\n## Documentation\n\n- [Docs Site](https://maxatwork.github.io/form2js/) - overview, installation, unified playground, and published API reference.\n- [Migration Guide](https://maxatwork.github.io/form2js/migrate/) - map old `form2js` and `jquery.toObject` usage to the current package family.\n- [API Reference Source](docs/api-index.md) - markdown source for the published API docs page.\n\n## Migration from Legacy\n\nIf you are moving from the archived single-package version, start with the [migration guide](https://maxatwork.github.io/form2js/migrate/).\n\nQuick package map:\n\n- Legacy browser `form2js(...)` usage -\u003e `@form2js/dom`\n- Legacy jQuery `$(\"#form\").toObject()` usage -\u003e `@form2js/jquery`\n- Server or pipeline `FormData` parsing -\u003e `@form2js/form-data`\n- React submit handling -\u003e `@form2js/react`\n- Object back into fields -\u003e `@form2js/js2form`\n\nThe current project keeps the naming rules and core parsing model, but splits the old browser-era API into environment-specific packages.\n\n## Packages\n\n| Package | npm | Purpose | Module | Standalone | Node.js |\n| ------- | --- | ------- | ------ | ---------- | ------- |\n| [`@form2js/react`](https://www.npmjs.com/package/@form2js/react) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Freact?label=npm)](https://www.npmjs.com/package/@form2js/react) | React submit hook with parsing/validation state | Yes | No | Browser-focused |\n| [`@form2js/dom`](https://www.npmjs.com/package/@form2js/dom) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Fdom?label=npm)](https://www.npmjs.com/package/@form2js/dom) | Extract DOM fields to object (`formToObject`, `form2js`) | Yes | Yes | With DOM shim (`jsdom`) |\n| [`@form2js/form-data`](https://www.npmjs.com/package/@form2js/form-data) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Fform-data?label=npm)](https://www.npmjs.com/package/@form2js/form-data) | Convert `FormData`/entries to object | Yes | No | Yes |\n| [`@form2js/js2form`](https://www.npmjs.com/package/@form2js/js2form) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Fjs2form?label=npm)](https://www.npmjs.com/package/@form2js/js2form) | Populate DOM fields from object (`objectToForm`, `js2form`) | Yes | No | With DOM shim (`jsdom`) |\n| [`@form2js/core`](https://www.npmjs.com/package/@form2js/core) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Fcore?label=npm)](https://www.npmjs.com/package/@form2js/core) | Path parsing and object transformation engine | Yes | No | Yes |\n| [`@form2js/jquery`](https://www.npmjs.com/package/@form2js/jquery) | [![npm version](https://img.shields.io/npm/v/%40form2js%2Fjquery?label=npm)](https://www.npmjs.com/package/@form2js/jquery) | jQuery plugin adapter (`$.fn.toObject`) | Yes | Yes | Browser-focused |\n\n## Installation\n\nInstall only what you need:\n\n```bash\nnpm install @form2js/react react\nnpm install @form2js/dom\nnpm install @form2js/form-data\nnpm install @form2js/js2form\nnpm install @form2js/core\nnpm install @form2js/jquery jquery\n```\n\nFor browser standalone usage, use script builds where available:\n\n- `@form2js/dom`: `dist/standalone.global.js`\n- `@form2js/jquery`: `dist/standalone.global.js`\n\n## Usage\n\n### `@form2js/dom`\n\nHTML used in examples:\n\n```html\n\u003cform id=\"profileForm\"\u003e\n  \u003cinput name=\"person.name.first\" value=\"Esme\" /\u003e\n  \u003cinput name=\"person.name.last\" value=\"Weatherwax\" /\u003e\n  \u003clabel\n    \u003e\u003cinput type=\"checkbox\" name=\"person.tags[]\" value=\"witch\" checked /\u003e\n    witch\u003c/label\n  \u003e\n\u003c/form\u003e\n```\n\nModule:\n\n```ts\nimport { formToObject } from \"@form2js/dom\";\n\nconst result = formToObject(document.getElementById(\"profileForm\"));\n// =\u003e { person: { name: { first: \"Esme\", last: \"Weatherwax\" }, tags: [\"witch\"] } }\n```\n\nStandalone:\n\n```html\n\u003cscript src=\"https://unpkg.com/@form2js/dom/dist/standalone.global.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const result = formToObject(document.getElementById(\"profileForm\"));\n  // or form2js(...)\n\u003c/script\u003e\n```\n\n### `@form2js/form-data`\n\nModule (browser or Node 18+):\n\n```ts\nimport { formDataToObject } from \"@form2js/form-data\";\n\nconst fd = new FormData(formElement);\nconst result = formDataToObject(fd);\n```\n\nNode.js note:\n\n- Node 18+ has global `FormData`.\n- You can also pass iterable entries directly, which is handy in server pipelines:\n\n```ts\nimport { entriesToObject } from \"@form2js/form-data\";\n\nconst result = entriesToObject([\n  [\"person.name.first\", \"Sam\"],\n  [\"person.roles[]\", \"captain\"],\n]);\n// =\u003e { person: { name: { first: \"Sam\" }, roles: [\"captain\"] } }\n```\n\nWith schema validation (works with Zod or any `{ parse(unknown) }` schema):\n\n```ts\nimport { z } from \"zod\";\nimport { formDataToObject } from \"@form2js/form-data\";\n\nconst PersonSchema = z.object({\n  person: z.object({\n    age: z.coerce.number().int().min(0)\n  })\n});\n\nconst result = formDataToObject([[\"person.age\", \"42\"]], {\n  schema: PersonSchema\n});\n// =\u003e { person: { age: 42 } }\n```\n\nStandalone:\n\n- Not shipped for this package. Use module imports.\n\n### `@form2js/react`\n\nModule:\n\n```ts\nimport { z } from \"zod\";\nimport { useForm2js } from \"@form2js/react\";\n\nconst FormDataSchema = z.object({\n  person: z.object({\n    name: z.object({\n      first: z.string().min(1)\n    })\n  })\n});\n\nexport function ProfileForm(): React.JSX.Element {\n  const { onSubmit, isSubmitting, isError, error, isSuccess, reset } = useForm2js(\n    async (data) =\u003e {\n      // data is inferred from schema when schema is provided\n      await sendFormData(data);\n    },\n    {\n      schema: FormDataSchema\n    }\n  );\n\n  return (\n    \u003cform\n      onSubmit={(event) =\u003e {\n        void onSubmit(event);\n      }}\n    \u003e\n      \u003cinput name=\"person.name.first\" defaultValue=\"Sam\" /\u003e\n      \u003cbutton type=\"submit\" disabled={isSubmitting}\u003e\n        {isSubmitting ? \"Saving...\" : \"Save\"}\n      \u003c/button\u003e\n      {isError ? \u003cp\u003e{String(error)}\u003c/p\u003e : null}\n      {isSuccess ? \u003cp\u003eSaved\u003c/p\u003e : null}\n      \u003cbutton type=\"button\" onClick={reset}\u003e\n        Reset state\n      \u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\n### `@form2js/jquery`\n\nHTML used in examples:\n\n```html\n\u003cform id=\"profileForm\"\u003e\n  \u003cinput name=\"person.name.first\" value=\"Sam\" /\u003e\n  \u003cinput name=\"person.name.last\" value=\"Vimes\" /\u003e\n\u003c/form\u003e\n```\n\nModule:\n\n```ts\nimport $ from \"jquery\";\nimport { installToObjectPlugin } from \"@form2js/jquery\";\n\ninstallToObjectPlugin($);\nconst data = $(\"#profileForm\").toObject({ mode: \"first\" });\n// =\u003e { person: { name: { first: \"Sam\", last: \"Vimes\" } } }\n```\n\nStandalone:\n\n```html\n\u003cscript src=\"https://code.jquery.com/jquery-3.7.1.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/@form2js/jquery/dist/standalone.global.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const data = $(\"#profileForm\").toObject({ mode: \"combine\" });\n\u003c/script\u003e\n```\n\n### `@form2js/js2form`\n\nHTML used in examples (before calling `objectToForm`):\n\n```html\n\u003cform id=\"profileForm\"\u003e\n  \u003cinput name=\"person.name.first\" /\u003e\n  \u003cinput name=\"person.name.last\" /\u003e\n\u003c/form\u003e\n```\n\nModule:\n\n```ts\nimport { objectToForm } from \"@form2js/js2form\";\n\nobjectToForm(document.getElementById(\"profileForm\"), {\n  person: { name: { first: \"Tiffany\", last: \"Aching\" } },\n});\n// fields are now populated in the form\n```\n\nStandalone:\n\n- Not shipped as a dedicated global bundle. Use module imports.\n\n### `@form2js/core`\n\nModule:\n\n```ts\nimport { entriesToObject, objectToEntries } from \"@form2js/core\";\n\nconst data = entriesToObject([\n  { key: \"person.name.first\", value: \"Vimes\" },\n  { key: \"person.tags[]\", value: \"watch\" },\n]);\n\nconst pairs = objectToEntries(data);\n```\n\nNode.js:\n\n- Fully supported (no DOM dependency).\n\nStandalone:\n\n- Not shipped for this package. Use module imports.\n\n## Legacy behavior notes\n\nCompatibility with the old project is intentional.\n\n- Name paths define output shape (`person.name.first`).\n- Array and indexed syntax is preserved (`items[]`, `items[5].name`).\n- Rails-style names are supported (`rails[field][value]`).\n- DOM extraction follows native browser form submission semantics for checkbox and radio values.\n- Unsafe key path segments (`__proto__`, `prototype`, `constructor`) are rejected by default.\n- This library does data shaping, not JSON/XML serialization.\n\n## Design boundaries and non-goals\n\nThese boundaries are intentional and are used for issue triage.\n\n- Sparse indexes are compacted in first-seen order (`items[5]`, `items[8]` -\u003e `items[0]`, `items[1]`).\n- Type inference is minimal by design; DOM extraction keeps native string values instead of coercing checkbox/radio fields.\n- Unchecked indexed controls are omitted and therefore do not reserve compacted array slots; include another submitted field when row identity matters.\n- `formToObject` reads successful form control values, not option labels. Disabled controls (including disabled fieldset descendants) and button-like inputs are excluded unless you explicitly opt in to disabled values.\n- `extractPairs`/`formToObject` support `nodeCallback`; return `SKIP_NODE` to exclude a node entirely, or `{ name|key, value }` to inject a custom entry.\n- Parser inputs reject unsafe path segments by default. Use `allowUnsafePathSegments: true` only with trusted inputs.\n- `objectToForm` supports `nodeCallback`; returning `false` skips the default assignment for that node.\n- `objectToForm` sets form control state and values; it does not dispatch synthetic `change` or `input` events.\n- Empty collections are not synthesized when no matching fields are present (for example, unchecked checkbox groups).\n- Dynamic key/value remapping (for example, converting `key`/`val` fields into arbitrary object keys) is application logic.\n- For file payloads and richer multipart semantics, use `FormData` and `@form2js/form-data`.\n\n## Contributing\n\n### Setup\n\n```bash\nnpm ci\n```\n\n### Local checks\n\n```bash\nnpm run lint\nnpm run typecheck\nnpm run test\nnpm run build\nnpm run pack:dry-run\n```\n\n### Local docs site\n\n```bash\nnpm run docs\nnpm run docs:build\n```\n\nThe homepage includes the unified playground for `@form2js/react`, `@form2js/dom`, `@form2js/jquery`, `@form2js/js2form`, `@form2js/core`, and `@form2js/form-data`.\n\n### GitHub Pages docs site\n\nThe published docs site is deployed by `.github/workflows/pages.yml`.\n\n- Trigger: pushes to `master` that touch `apps/docs/**`, `docs/**`, `packages/**`, `.github/workflows/pages.yml`, `package.json`, `package-lock.json`, `turbo.json`, or `tsconfig.base.json`, plus manual `workflow_dispatch`.\n- Output: `apps/docs/dist`.\n- URL: `https://maxatwork.github.io/form2js/`.\n\nIn repository settings, set Pages source to `GitHub Actions` once, and then the workflow handles updates.\n\n### Before opening a PR\n\n1. Keep changes focused to one problem area where possible.\n2. Add or update tests for behavior changes.\n3. Add a changeset (`npm run changeset`) for user-visible changes.\n4. Include migration notes in README if behavior or API changes.\n\n### Filing PRs and issues\n\nPlease include:\n\n- Clear expected vs actual behavior.\n- Minimal reproduction (HTML snippet or input entries).\n- Package name and version.\n- Environment (`node -v`, browser/version if relevant).\n\n## Release workflow\n\n- CI runs lint, typecheck, test, build, and package dry-run.\n- Releases are managed with Changesets and the published `@form2js/*` packages are versioned in lockstep.\n\n## Scope rewrite helper\n\nDefault scope is `@form2js/*`.\n\nIf you need to publish under another scope:\n\n```bash\nnpm run scope:rewrite -- --scope @your-scope --dry-run\nnpm run scope:rewrite -- --scope @your-scope\n```\n\nThis rewrites package names, internal dependencies, and import references.\n\n## License\n\nMIT, see `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxatwork%2Fform2js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxatwork%2Fform2js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxatwork%2Fform2js/lists"}