{"id":27091287,"url":"https://github.com/wavelop/dynamic-form","last_synced_at":"2025-10-24T18:48:32.116Z","repository":{"id":54869428,"uuid":"254960211","full_name":"Wavelop/dynamic-form","owner":"Wavelop","description":"Build forms 📄   with React 🚀","archived":false,"fork":false,"pushed_at":"2023-11-10T12:02:46.000Z","size":371,"stargazers_count":4,"open_issues_count":7,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-16T11:31:56.718Z","etag":null,"topics":["forms","react","react-hooks","reactjs"],"latest_commit_sha":null,"homepage":"https://wavelop.com","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/Wavelop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2020-04-11T21:41:30.000Z","updated_at":"2021-01-23T19:32:36.000Z","dependencies_parsed_at":"2024-09-20T20:00:56.433Z","dependency_job_id":"5e87da3a-b5f1-435d-aaa1-f9235bc860f0","html_url":"https://github.com/Wavelop/dynamic-form","commit_stats":{"total_commits":97,"total_committers":3,"mean_commits":"32.333333333333336","dds":"0.030927835051546393","last_synced_commit":"9c158bfbde3c2607a7938944814e702d709aad0c"},"previous_names":["blundert/dynamic-form"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wavelop%2Fdynamic-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wavelop%2Fdynamic-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wavelop%2Fdynamic-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wavelop%2Fdynamic-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Wavelop","download_url":"https://codeload.github.com/Wavelop/dynamic-form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247447428,"owners_count":20940318,"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":["forms","react","react-hooks","reactjs"],"created_at":"2025-04-06T07:26:13.325Z","updated_at":"2025-10-24T18:48:32.025Z","avatar_url":"https://github.com/Wavelop.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- PROJECT SHIELDS --\u003e\n\u003c!--\n*** I'm using markdown \"reference style\" links for readability.\n*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).\n*** See the bottom of this document for the declaration of the reference variables\n*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.\n*** https://www.markdownguide.org/basic-syntax/#reference-style-links\n--\u003e\n[![NPM][node-package-shield]][node-package-url]\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n[![LinkedIn][linkedin-shield]][linkedin-url]\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Wavelop/dynamic-form\"\u003e\n    \u003cimg src=\"images/logo.svg\" alt=\"Logo\" width=\"300\"\u003e\n  \u003c/a\u003e\n\n  \u003ch3 align=\"center\"\u003eDynamic Form\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    Build forms 📄 with React 🚀\n    \u003cbr /\u003e\n    \u003ca href=\"https://dynamic-form-wavelop.firebaseapp.com/\"\u003e\u003cstrong\u003eExplore the docs »\u003c/strong\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://dynamic-form-wavelop-example.firebaseapp.com/\"\u003eView Demo\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/Wavelop/dynamic-form/issues\"\u003eOpen an Issue\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/Wavelop/dynamic-form/issues\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n## 📑 Table of Contents\n\n- [📑 Table of Contents](#-table-of-contents)\n- [🔍 About The Project](#-about-the-project)\n  - [🔧 Built With](#-built-with)\n- [🔫 Getting Started](#-getting-started)\n  - [👶 Prerequisites](#-prerequisites)\n  - [💻 Installation](#-installation)\n- [🚲 Usage](#-usage)\n- [📚 Documentation](#-documentation)\n- [🥇 Add your values](#-add-your-values)\n- [🏘 Using existing components](#-using-existing-components)\n  - [🧱 Build your custom components](#-build-your-custom-components)\n  - [💫 Repeater components](#-repeater-components)\n- [✈️ Roadmap](#️-roadmap)\n- [🚑 Contributing](#-contributing)\n- [💰 License](#-license)\n- [📞 Contact](#-contact)\n\n\n\u003c!-- ABOUT THE PROJECT --\u003e\n## 🔍 About The Project\n\nThis project implements a form generator with React Hooks. \n\n### 🔧 Built With\n\n* [React](https://reactjs.org/)\n* [React Hooks](https://reactjs.org/docs/hooks-intro.html)\n\n\n\u003c!-- GETTING STARTED --\u003e\n## 🔫 Getting Started\n\nTo start to use the library, follow these simple steps. \n\n### 👶 Prerequisites\n\nThis is an example of how to list things you need to use the software and how to install them.\n\n```bash\nnpm install react react-dom\n```\n\n### 💻 Installation\n \n```bash\nnpm install @wavelop/dynamic-form @wavelop/dynamic-form-base-components\n```\n\n\n\u003c!-- USAGE EXAMPLES --\u003e\n## 🚲 Usage\n\nCreate a configuration file: \n\n```js\n// config.js\n\nimport { Input } from \"@wavelop/dynamic-form-base-components\";\nimport { validations } from \"@wavelop/dynamic-form\";\n\nconst { required, pattern } = validations;\n\nexport default [\n  {\n    name: \"email\",\n    label: \"Email\",\n    helperText: \"Write your email\",\n    tag: Input,\n    type: \"email\",\n    defaultValue: \"info@wavelop.com\",\n    validations: [\n      {\n        kind: required,\n        message: \"Email is required\"\n      },\n      {\n        kind: pattern,\n        reg: /^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$/,\n        negate: true,\n        message: \"You must type an email i.e. test@test.com\"\n      }\n    ]\n  }\n];\n```\n\n```jsx\n\nimport React, { useRef } from \"react\";\nimport PropTypes from \"prop-types\";\n\nimport {\n  DynamicForm,\n  useDynamicForm,\n  withDynamicForm\n} from \"@wavelop/dynamic-form\";\n\nimport { form as formConfig } from \"./config.js\";\n\nfunction Example(props) {\n\n  const dynamicForm = useDynamicForm();\n\n  const onSubmit = event =\u003e {\n\n    event.preventDefault();\n\n    try {\n      const { state, stateCrypted } = dynamicForm.submit();\n      // Do something with you valid state...\n      console.log(state, stateCrypted);\n\n    } catch ({numberOfErrors, errors}) {\n      // Do something in case of error...\n      console.log(numberOfErrors, errors);\n    }\n  };\n\n  // Render\n  return (\n    \u003cform onSubmit={onSubmit}\u003e\n      \u003cDynamicForm\n        config={formConfig}\n        updateErrorAtBlur={true}\n        debug={true}\n      /\u003e\n\n      \u003cbutton\n        type=\"submit\"\n        onClick={onSubmit}\n      \u003e\n        Confirm\n      \u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n\nExample.propTypes = {\n  classes: PropTypes.object.isRequired\n};\n\nexport default withDynamicForm()(Example);\n```\n\n_For more examples, please refer to the [Example folder](https://github.com/Wavelop/dynamic-form/tree/master/example)_.\n\n## 📚 Documentation\n\nCheck out our [documentation website](https://dynamic-form-wavelop.firebaseapp.com/).\n\n## 🥇 Add your values\n\nUse the `deafultValue` option to add an initial value to a field. \n\n```javascript\n// config.js\n...\nfields: [\n  {\n    name: \"field1_row2\",\n    label: \"field1_row2\",\n    helperText: \"field1_row2\",\n    tag: \"notRow\",\n    defaultValue: \"hello\",\n  }\n]\n...\n```\n\n## 🏘 Using existing components\n\nCore functionalities can be used with exinsing components, as with the one of the package `@wavelop/dynamic-form-base-components` or you can create your custom components to inject inside the configuration. \n\n### 🧱 Build your custom components\n\nWIP.\n\n### 💫 Repeater components\n\nThere are more way to create repeater components:\n\n1. Update your configuration dinamically adding new piece of configuration. To group state results you should use `rowOptions`, both on row container and field. \n\n\u003cdetails\u003e\n \u003csummary\u003eExample point 1\u003c/summary\u003e\n\nIn the configuration create the initial configuration:\n```js\n// config.js\n\n// import and other stuff..\n\nexport default [ \n  {\n    name: \"row1\",\n    tag: \"row\",\n    fields: [\n      {\n        name: \"field1_row1\",\n        label: \"field1_row1\",\n        helperText: \"field1_row1\",\n        tag: \"notRow\"\n      }\n    ]\n  },\n  {\n    name: \"row2\",\n    tag: \"row\",\n    rowOptions: { // \u003c--- Add rowOptions to the row container and use the property groupIn\n      groupIn: \"rowsToGroupAsArray\"\n    },\n    fields: [\n      {\n        name: \"field1_row2\",\n        label: \"field1_row2\",\n        helperText: \"field1_row2\",\n        tag: \"notRow\",\n        rowOptions: { // \u003c--- Add rowOptions to the child and use the property alternativeName\n          alternativeName: \"field1\",\n        }\n      }\n    ]\n  },\n  {\n    name: \"row3\",\n    tag: \"row\",\n    rowOptions: { // \u003c--- Add rowOptions to the row container and use the property groupIn\n      groupIn: \"rowsToGroupAsArray\"\n    },\n    fields: [\n      {\n        name: \"field1_row3\",\n        label: \"field1_row3\",\n        helperText: \"field1_row3\",\n        tag: Input,\n        rowOptions: { // \u003c--- Add rowOptions to the child and use the property alternativeName\n          alternativeName: \"field1\",\n        }\n      }\n    ]\n  },\n  {\n    name: \"row4\",\n    tag: \"row\",\n    rowOptions: { // \u003c--- Add rowOptions to the row container and use the property groupIn\n      groupIn: \"rowsToGroupAsArray2\"\n    },\n    fields: [\n      {\n        name: \"field2_row4\",\n        label: \"field2_row4\",\n        helperText: \"field2_row4\",\n        tag: \"notRow\",\n        rowOptions: { // \u003c--- Add rowOptions to the child and use the property alternativeName\n          alternativeName: \"field2\",\n        }\n      },\n    ]\n  },\n  {\n    name: \"row5\",\n    tag: \"row\",\n    rowOptions: { // \u003c--- Add rowOptions to the row container and use the property groupIn\n      groupIn: \"rowsToGroupAsArray2\"\n    },\n    fields: [\n      {\n        name: \"field2_row5\",\n        label: \"field2_row5\",\n        helperText: \"field2_row5\",\n        tag: \"notRow\",\n        rowOptions: { // \u003c--- Add rowOptions to the child and use the property alternativeName\n          alternativeName: \"field2\",\n        }\n      },\n    ]\n  }\n];\n```\n\nWhere you use DynamicForm and execute submit having this current state: \n\n```javascript\n{\n  \"field1_row1\": \"1a\",\n  \"field1_row2\": \"2a\",\n  \"field1_row3\": \"3a\",\n  \"field2_row4\": \"4b\",\n  \"field2_row5\": \"5b\",\n}\n```\n\n```javascript\n// Other stuff...\n\nconst { state, stateGroupedByRows, stateGroupedByRowsGroupIn } = dynamicForm.submit();\n\nconsole.log(state);\n/**\n{\n  \"field1_row1\": \"1a\",\n  \"field1_row2\": \"2a\",\n  \"field1_row3\": \"3a\",\n  \"field2_row4\": \"4b\",\n  \"field2_row5\": \"5b\",\n}\n**/ \n\nconsole.log(groupByRows);\n/**\n{\n  \"row1\": {\n    \"field1_row1\": \"1a\",\n  },\n  \"row2\": {\n    \"field1_row2\": \"2a\",\n  },\n  \"row3\": {\n    \"field1_row3\": \"3a\",\n  },\n  \"row4\": {\n    \"field2_row4\": \"4b\",\n  },\n  \"row5\": {\n    \"field2_row5\": \"5b\"\n  }\n}\n**/ \n\nconsole.log(groupByRowsGroupIn);\n/**\n{\n  \"row1\": {\n    \"field1_row1\": \"1a\",\n    \"field2_row1\": \"1b\"\n  },\n  \"rowsToGroupAsArray\": [\n    {\n      \"field1\": \"2a\",\n    },\n    {\n      \"field1\": \"3a\",\n    }\n  ],\n  \"rowsToGroupAsArray2\": [\n    {\n      \"field2\": \"4b\"\n    },\n    {\n      \"field2\": \"5b\"\n    }\n  ]\n}\n**/ \n```\n\nThink now to add to the above configuration a new row: \n\n```javascript\n\nconfiguration.push({\n  name: \"row6\",\n  tag: \"row\",\n  rowOptions: { // \u003c--- Add rowOptions to the row container and use the property groupIn\n    groupIn: \"rowsToGroupAsArray\"\n  },\n  fields: [\n    {\n      name: \"field1_row6\",\n      label: \"field1_row6\",\n      helperText: \"field1_row6\",\n      tag: \"notRow\",\n      rowOptions: { // \u003c--- Add rowOptions to the child and use the property alternativeName\n        alternativeName: \"field1\",\n      }\n    },\n  ]\n});\n```\n\nAt the submit we will have (considering to have written `6a` inside the new input): \n\n```javascript\n// Other stuff...\n\nconst { state, stateGroupedByRows, stateGroupedByRowsGroupIn } = dynamicForm.submit();\n\nconsole.log(state);\n/**\n{\n  \"field1_row1\": \"1a\",\n  \"field1_row2\": \"2a\",\n  \"field1_row3\": \"3a\",\n  \"field2_row4\": \"4b\",\n  \"field2_row5\": \"5b\",\n  \"field1_row6\": \"6a\",\n}\n**/ \n\nconsole.log(groupByRows);\n/**\n{\n  \"row1\": {\n    \"field1_row1\": \"1a\",\n  },\n  \"row2\": {\n    \"field1_row2\": \"2a\",\n  },\n  \"row3\": {\n    \"field1_row3\": \"3a\",\n  },\n  \"row4\": {\n    \"field2_row4\": \"4b\",\n  },\n  \"row5\": {\n    \"field2_row5\": \"5b\"\n  },\n  \"row6\": {\n    \"field1_row6\": \"6a\"\n  }\n}\n**/ \n\nconsole.log(groupByRowsGroupIn);\n/**\n{\n  \"row1\": {\n    \"field1_row1\": \"1a\",\n    \"field2_row1\": \"1b\"\n  },\n  \"rowsToGroupAsArray\": [\n    {\n      \"field1\": \"2a\",\n    },\n    {\n      \"field1\": \"3a\",\n    },\n    {\n      \"field1\": \"6a\",\n    }\n  ],\n  \"rowsToGroupAsArray2\": [\n    {\n      \"field2\": \"4b\"\n    },\n    {\n      \"field2\": \"5b\"\n    }\n  ]\n}\n**/\n```\n\n\u003c/details\u003e\n\n\n\u003cbr/\u003e\n1. (WIP) Create a custom component that implement the repeater logic and it update the form values.\n\n\u003c!-- ROADMAP --\u003e\n## ✈️ Roadmap\n\nSee the [open issues](https://github.com/Wavelop/dynamic-form/issues) for a list of proposed features (and known issues).\n\n\u003c!-- CONTRIBUTING --\u003e\n## 🚑 Contributing\n\nContributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nRead our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes.\n\n\u003c!-- LICENSE --\u003e\n## 💰 License\n\nDistributed under the MIT License. See `LICENSE` for more information.\n\n\u003c!-- CONTACT --\u003e\n## 📞 Contact\n\nMatteo Granzotto - [@wavelop](https://twitter.com/blundert) - [matteo.granzotto@wavelop.com](mailto:matteo.granzotto@wavelop.com)\n\n[Wavelop](wavelop.com) - [info@wavelop.com](mailto:info@wavelop.com)\n\nProject Link: [https://github.com/Wavelop/dynamic-form](https://github.com/Wavelop/dynamic-form)\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n[node-package-shield]: https://img.shields.io/npm/v/@wavelop/dynamic-form.svg\n[node-package-url]: https://www.npmjs.com/package/@wavelop/dynamic-form\n[contributors-shield]: https://img.shields.io/github/contributors/Wavelop/dynamic-form.svg?style=flat-square\n[contributors-url]: https://github.com/Wavelop/dynamic-form/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/Wavelop/dynamic-form.svg?style=flat-square\n[forks-url]: https://github.com/Wavelop/dynamic-form/network/members\n[stars-shield]: https://img.shields.io/github/stars/Wavelop/dynamic-form.svg?style=flat-square\n[stars-url]: https://github.com/Wavelop/dynamic-form/stargazers\n[issues-shield]: https://img.shields.io/github/issues/Wavelop/dynamic-form.svg?style=flat-square\n[issues-url]: https://github.com/Wavelop/dynamic-form/issues\n[license-shield]: https://img.shields.io/github/license/Wavelop/dynamic-form.svg?style=flat-square\n[license-url]: https://github.com/Wavelop/dynamic-form/blob/master/LICENSE\n[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square\u0026logo=linkedin\u0026colorB=555\n[linkedin-url]: https://www.linkedin.com/company/wavelop","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavelop%2Fdynamic-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwavelop%2Fdynamic-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavelop%2Fdynamic-form/lists"}