├── .github ├── stale.yml └── workflows │ ├── ci.yml │ └── lock-issue.yml ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── contentlayer.config.ts ├── eslint.config.mjs ├── next-env.d.ts ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── public └── images │ ├── Logo.svg │ ├── casinoreviews.png │ ├── dev-tool.png │ ├── follower24.png │ ├── formik.min.png │ ├── gatsby-icon.png │ ├── hookForm.png │ ├── hookform.min.png │ ├── logo │ ├── react-hook-form-logo-grey.png │ ├── react-hook-form-logo-grey.svg │ ├── react-hook-form-logo-only-grey.png │ ├── react-hook-form-logo-only-grey.svg │ ├── react-hook-form-logo-only.png │ ├── react-hook-form-logo-only.svg │ ├── react-hook-form-logo.png │ └── react-hook-form-logo.svg │ ├── open-link.svg │ ├── react-hook-form-og.png │ ├── reduxform.min.png │ ├── route4me.png │ ├── sanity.png │ └── twicsy.png ├── src ├── components │ ├── Admonition.module.css │ ├── Admonition.tsx │ ├── ApiFormState.tsx │ ├── ApiGallery.module.css │ ├── ApiGallery.tsx │ ├── ApiPage.module.css │ ├── Bday.module.css │ ├── BeekaiBuilderPage.module.css │ ├── BeekaiBuilderPage.tsx │ ├── BuilderPage.module.css │ ├── BuilderPage.tsx │ ├── ClipBoard.tsx │ ├── CodeArea.module.css │ ├── CodeArea.tsx │ ├── CodeCompareSection.module.css │ ├── CodeCompareSection.tsx │ ├── CodePerfCompareSection.module.css │ ├── CodePerfCompareSection.tsx │ ├── CodeSandbox.tsx │ ├── DevToolFeaturesList.module.css │ ├── DevToolFeaturesList.tsx │ ├── DevTools.module.css │ ├── DevTools.tsx │ ├── FeatureList.module.css │ ├── FeaturesList.tsx │ ├── Footer.module.css │ ├── Footer.tsx │ ├── Form.module.css │ ├── Form.tsx │ ├── FormFields.module.css │ ├── FormFields.tsx │ ├── FormStateApi.tsx │ ├── FormStateTable.tsx │ ├── GetStarted.module.css │ ├── Header.module.css │ ├── Header.tsx │ ├── HomePage.module.css │ ├── HomePage.tsx │ ├── IsolateRender.module.css │ ├── IsolateRender.tsx │ ├── Menu │ │ ├── Menu.tsx │ │ ├── MenuLinks.ts │ │ ├── SideMenu.module.css │ │ └── index.ts │ ├── Nav.module.css │ ├── Nav.tsx │ ├── Popup.module.css │ ├── Popup.tsx │ ├── ResourceList.tsx │ ├── ResourcePage.module.css │ ├── ResourcePageArticles.tsx │ ├── ResourcePageBindings.tsx │ ├── ResourcePageNewsletter.tsx │ ├── ResourcePageVideos.tsx │ ├── Search.module.css │ ├── Search.tsx │ ├── SortableContainer.module.css │ ├── SortableContainer.tsx │ ├── StarRepo.tsx │ ├── TabGroup.module.css │ ├── TabGroup.tsx │ ├── Toggle.module.css │ ├── Toggle.tsx │ ├── TypeText.tsx │ ├── UseController.tsx │ ├── UseControllerContent.tsx │ ├── UseControllerMethods.tsx │ ├── UseFieldArray.tsx │ ├── UseFieldArrayContent.tsx │ ├── Watcher.module.css │ ├── Watcher.tsx │ ├── codeExamples │ │ ├── dependantFieldsTS.tsx │ │ ├── devTool.tsx │ │ ├── formState.ts │ │ ├── formStateTs.ts │ │ ├── formStateUseEffect.ts │ │ ├── formStateUseEffectTs.ts │ │ ├── reactHookFormCode.ts │ │ ├── useController.ts │ │ ├── useControllerCheckboxes.ts │ │ ├── useControllerTs.ts │ │ ├── useFieldArray.ts │ │ ├── useFieldArrayArgument.ts │ │ ├── useFieldArrayConditional.ts │ │ ├── useFieldArrayFocus.ts │ │ ├── useFieldArrayPreview.ts │ │ ├── useFieldArrayTS.ts │ │ └── useFormState.ts │ ├── general-observer.tsx │ ├── layout.css │ ├── layout.tsx │ ├── learnMore.tsx │ ├── logic │ │ ├── generateCode.ts │ │ └── getEditLink.tsx │ ├── mdx │ │ ├── code.tsx │ │ ├── mdx.tsx │ │ ├── pre.tsx │ │ ├── theme.ts │ │ └── youtube.tsx │ ├── selectNav.module.css │ ├── selectNav.tsx │ ├── seo.tsx │ ├── sponsorsList.module.css │ ├── sponsorsList.tsx │ └── utils │ │ ├── copyClipBoard.ts │ │ ├── goToBuilder.ts │ │ └── useWindowSize.ts ├── content │ ├── advanced-usage.mdx │ ├── docs │ │ ├── createFormControl.mdx │ │ ├── formprovider.mdx │ │ ├── usecontroller │ │ │ └── controller.mdx │ │ ├── useform.mdx │ │ ├── useform │ │ │ ├── clearerrors.mdx │ │ │ ├── control.mdx │ │ │ ├── form.mdx │ │ │ ├── formstate.mdx │ │ │ ├── getfieldstate.mdx │ │ │ ├── getvalues.mdx │ │ │ ├── handlesubmit.mdx │ │ │ ├── register.mdx │ │ │ ├── reset.mdx │ │ │ ├── resetfield.mdx │ │ │ ├── seterror.mdx │ │ │ ├── setfocus.mdx │ │ │ ├── setvalue.mdx │ │ │ ├── subscribe.mdx │ │ │ ├── trigger.mdx │ │ │ ├── unregister.mdx │ │ │ └── watch.mdx │ │ ├── useformcontext.mdx │ │ ├── useformstate.mdx │ │ ├── useformstate │ │ │ └── errormessage.mdx │ │ └── usewatch.mdx │ ├── faqs.mdx │ ├── get-started.mdx │ └── ts.mdx ├── data │ ├── api.tsx │ ├── builder.tsx │ ├── devtools.tsx │ ├── generic.tsx │ ├── home.tsx │ ├── nav.tsx │ └── resources.tsx ├── pages │ ├── 404.module.css │ ├── 404.tsx │ ├── [...slug].tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── about-us.tsx │ ├── dev-tools.tsx │ ├── docs.tsx │ ├── docs │ │ ├── usecontroller.tsx │ │ └── usefieldarray.tsx │ ├── form-builder.tsx │ ├── index.tsx │ ├── media.module.css │ ├── media.tsx │ ├── migrate-v6-to-v7.tsx │ └── resources │ │ ├── 3rd-party-bindings.tsx │ │ ├── articles.tsx │ │ ├── newsletters.tsx │ │ └── videos.tsx ├── state │ └── formData.ts ├── styles │ ├── breakpoints.ts │ ├── button.module.css │ ├── colors.ts │ ├── container.module.css │ ├── table.module.css │ └── typography.module.css └── types │ ├── global.d.ts │ ├── little-state-machine.d.ts │ └── types.ts └── tsconfig.json /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 20 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 3 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | env: 12 | HUSKY: 0 13 | 14 | jobs: 15 | check: 16 | name: Run ${{ matrix.script }} code check 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 15 19 | 20 | strategy: 21 | fail-fast: true 22 | matrix: 23 | script: ["format", "lint:ci", "typecheck:ci"] 24 | 25 | steps: 26 | - name: Checkout repo 27 | uses: actions/checkout@v4 28 | 29 | - name: Setup pnpm 30 | uses: pnpm/action-setup@v4 31 | 32 | - name: Setup Node 33 | uses: actions/setup-node@v4 34 | with: 35 | node-version-file: ".nvmrc" 36 | cache: "pnpm" 37 | 38 | - name: Install Dependencies 39 | shell: bash 40 | run: pnpm install --frozen-lockfile 41 | 42 | - name: Run ${{ matrix.script }} 43 | run: | 44 | pnpm run ${{ matrix.script }} 45 | -------------------------------------------------------------------------------- /.github/workflows/lock-issue.yml: -------------------------------------------------------------------------------- 1 | name: "Lock Issues" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0,6,12,18 * * *" 7 | 8 | permissions: 9 | issues: write 10 | 11 | concurrency: 12 | group: lock 13 | 14 | jobs: 15 | action: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: dessant/lock-threads@v3 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | issue-inactive-days: 60 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | .idea/ 26 | .idea 27 | .cache/ 28 | package-lock.json 29 | .vscode 30 | tsconfig.tsbuildinfo 31 | .next/ 32 | 33 | # contentlayer 34 | .contentlayer 35 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.18.1 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | .cache/ 3 | .contentlayer/ 4 | .next/ 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "endOfLine": "lf", 4 | "semi": false, 5 | "singleQuote": false, 6 | "tabWidth": 2, 7 | "trailingComma": "es5" 8 | } 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at bluebill1049@hotmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `React Hook Form` (The Docs) 2 | 3 | As the creators and maintainers of this project, we want to ensure that `react-hook-form` lives and continues to grow and evolve. We would like to encourage everyone to help and support this library by contributing. 4 | 5 | ## Code contributions 6 | 7 | Here is a quick guide to doing code contributions to the library. 8 | 9 | 1. Fork and clone the repo to your local machine `git clone https://github.com/YOUR_GITHUB_USERNAME/website.git` 10 | 11 | 2. Create a new branch from `master` with a meaningful name for a new feature or an issue you want to work on: `git checkout -b your-meaningful-branch-name` 12 | 13 | 3. Install packages by running: 14 | 15 | ```shellscript 16 | pnpm install 17 | ``` 18 | 19 | 4. Startup a local version of the docs 20 | 21 | ```shellscript 22 | pnpm run dev 23 | ``` 24 | 25 | 5. Ensure your code is formatted properly 26 | 27 | ```shellscript 28 | pnpm run format 29 | ``` 30 | 31 | 6. Push your branch: `git push -u origin your-meaningful-branch-name` 32 | 33 | 7. Submit a pull request to the upstream react-hook-form repository. 34 | 35 | 8. Choose a descriptive title and describe your changes briefly. 36 | 37 | ## Testing production build 38 | 39 | To test the documentation production site on your local machine, 40 | first execute the build script: 41 | 42 | ```shellscript 43 | pnpm run build 44 | ``` 45 | 46 | Then start a local server which serves the file created by executing 47 | 48 | ```shellscript 49 | pnpm run start 50 | ``` 51 | 52 | ## Coding style 53 | 54 | Please follow the coding style of the project. 55 | React Hook Form uses prettier. 56 | If possible, enable the prettier plugin in your editor to get real-time feedback. The formatting can be run manually with the following command: 57 | 58 | ```shellscript 59 | pnpm run format:fix 60 | ``` 61 | 62 | ## License 63 | 64 | By contributing your code to the react-hook-form GitHub repository, you agree to license your contribution under the MIT license. 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-present Beier(Bill) Luo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | React Hook Form Logo - React hook custom hook for form validation 5 | 6 |

7 |
8 | 9 |

Performant, flexible and extensible forms with easy to use validation.

10 | 11 | ## Install 12 | 13 | ```shellscript 14 | pnpm install && pnpm dev 15 | ``` 16 | 17 | ## Backers 18 | 19 | Thanks goes to all our backers! [[Become a backer](https://opencollective.com/react-hook-form#backer)]. 20 | 21 | 22 | 23 | 24 | 25 | ## Contributors 26 | 27 | Thanks goes to these wonderful people. [[Become a contributor](https://github.com/react-hook-form/documentation/blob/master/CONTRIBUTING.md)]. 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /contentlayer.config.ts: -------------------------------------------------------------------------------- 1 | import { defineDocumentType, makeSource } from "contentlayer/source-files" 2 | import { remarkHeadingId } from "remark-custom-heading-id" 3 | import remarkGfm from "remark-gfm" 4 | import rehypeMdxCodeProps from "rehype-mdx-code-props" 5 | import emoji from "remark-emoji" 6 | import * as sidebar from "./src/components/Menu/MenuLinks" 7 | import type { Pages } from "./src/types/types" 8 | 9 | export const Doc = defineDocumentType(() => ({ 10 | name: "Doc", 11 | contentType: "mdx", 12 | filePathPattern: "**/*.mdx", 13 | fields: { 14 | title: { type: "string", required: true }, 15 | description: { type: "string", required: true }, 16 | sidebar: { 17 | type: "enum", 18 | options: [ 19 | "apiLinks", 20 | "advancedLinks", 21 | "tsLinks", 22 | "faqLinks", 23 | "getStartedLinks", 24 | ], 25 | required: true, 26 | }, 27 | }, 28 | computedFields: { 29 | slug: { 30 | type: "string", 31 | resolve: (doc) => `/${doc._raw.flattenedPath}`, 32 | }, 33 | slugAsParams: { 34 | type: "string", 35 | resolve: (doc) => doc._raw.flattenedPath.split("/").slice(1).join("/"), 36 | }, 37 | 38 | /** 39 | * Can't define value different from primitives, so hardcoding the correct type 40 | * @see https://github.com/contentlayerdev/contentlayer/issues/149 41 | */ 42 | segment: { 43 | type: "string[]" as "list", 44 | resolve: (doc) => doc._raw.flattenedPath.split("/"), 45 | }, 46 | pages: { 47 | type: "json", 48 | resolve: (doc) => { 49 | // added explicit type casting to keep track of this data structure 50 | // in case in the future contentlayer will support values different than primitives 51 | return sidebar[doc.sidebar] as unknown as Pages 52 | }, 53 | }, 54 | }, 55 | })) 56 | 57 | export default makeSource({ 58 | contentDirPath: "src/content", 59 | documentTypes: [Doc], 60 | mdx: { 61 | remarkPlugins: [remarkGfm, remarkHeadingId, emoji], 62 | rehypePlugins: [rehypeMdxCodeProps], 63 | }, 64 | }) 65 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import tseslint from "typescript-eslint" 3 | import { FlatCompat } from "@eslint/eslintrc" 4 | import eslintPluginJsxA11y from "eslint-plugin-jsx-a11y" 5 | import eslintPluginReact from "eslint-plugin-react" 6 | 7 | const compat = new FlatCompat({ 8 | baseDirectory: import.meta.dirname, 9 | }) 10 | 11 | export default tseslint.config( 12 | tseslint.configs.recommendedTypeChecked, 13 | 14 | ...compat.config({ 15 | extends: ["next"], 16 | rules: { 17 | // react 18 | "react/prop-types": "off", 19 | "react/no-unescaped-entities": "off", 20 | "react/jsx-curly-brace-presence": "warn", 21 | 22 | // jsx-ally is already included in next-js so 23 | // we are adding only recommended rules directly here 24 | ...eslintPluginJsxA11y.flatConfigs.recommended.rules, 25 | "jsx-a11y/no-onchange": "warn", 26 | 27 | // import 28 | "import/no-anonymous-default-export": "off", 29 | 30 | // next 31 | "@next/next/no-img-element": "off", 32 | }, 33 | }), 34 | 35 | // @ts-expect-error eslintPluginReact.configs.flat, but runtime is always defined 36 | eslintPluginReact.configs.flat["jsx-runtime"], 37 | 38 | { 39 | linterOptions: { 40 | reportUnusedDisableDirectives: "error", 41 | }, 42 | languageOptions: { 43 | parserOptions: { 44 | projectService: true, 45 | tsconfigRootDir: import.meta.dirname, 46 | }, 47 | }, 48 | rules: { 49 | // typescript 50 | "@typescript-eslint/explicit-function-return-type": "off", 51 | "@typescript-eslint/explicit-module-boundary-types": "off", 52 | "@typescript-eslint/interface-name-prefix": "off", 53 | "@typescript-eslint/no-floating-promises": "off", 54 | "@typescript-eslint/restrict-template-expressions": [ 55 | "error", 56 | { 57 | allowAny: false, 58 | allowBoolean: false, 59 | allowNever: false, 60 | allowNullish: false, 61 | allowNumber: true, 62 | allowRegExp: false, 63 | }, 64 | ], 65 | }, 66 | } 67 | ) 68 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next" 2 | import { withContentlayer } from "next-contentlayer" 3 | import withBundleAnalyzer from "@next/bundle-analyzer" 4 | 5 | const nextConfig: NextConfig = { 6 | eslint: { 7 | /** 8 | * Now eslint requires typecheck so we have to run build before executing lint 9 | * These check are performed via `.github/workflows/ci.yml` action 10 | * 11 | * @see https://github.com/react-hook-form/documentation/pull/1107 12 | */ 13 | ignoreDuringBuilds: true, 14 | }, 15 | typescript: { 16 | /** @see `eslint.ignoreDuringBuilds` comment */ 17 | ignoreBuildErrors: true, 18 | }, 19 | reactStrictMode: true, 20 | pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"], 21 | } 22 | 23 | const bundleAnalyzer = withBundleAnalyzer({ 24 | enabled: process.env.ANALYZE === "true", 25 | }) 26 | 27 | export default bundleAnalyzer(withContentlayer(nextConfig)) 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-hook-form-website", 3 | "description": "React hooks for form validation without the hassle.", 4 | "version": "6.0.1", 5 | "author": "beier luo", 6 | "dependencies": { 7 | "@hookform/devtools": "4.3.1", 8 | "@mdx-js/loader": "^2.3.0", 9 | "@mdx-js/react": "^2.3.0", 10 | "@next/mdx": "15.1.0", 11 | "class-variance-authority": "^0.6.1", 12 | "clsx": "^1.2.1", 13 | "contentlayer": "^0.3.4", 14 | "little-state-machine": "^4.8.1", 15 | "next": "^15.1.2", 16 | "next-contentlayer": "^0.3.4", 17 | "next-themes": "^0.2.1", 18 | "prism-react-renderer": "^2.4.1", 19 | "prismjs": "^1.30.0", 20 | "react": "18.3.1", 21 | "react-dom": "18.3.1", 22 | "react-github-btn": "1.4.0", 23 | "react-hook-form": "7.44.3", 24 | "react-simple-animate": "^3.5.3", 25 | "react-simple-img": "3.0.0", 26 | "react-sortablejs": "1.5.1", 27 | "rehype-mdx-code-props": "^1.0.0", 28 | "remark-custom-heading-id": "^1.0.1", 29 | "remark-emoji": "^3.1.2", 30 | "remark-gfm": "^3.0.1", 31 | "sortablejs": "1.15.0" 32 | }, 33 | "devDependencies": { 34 | "@next/bundle-analyzer": "15.1.0", 35 | "@types/eslint-plugin-jsx-a11y": "6.10.0", 36 | "@types/eslint__eslintrc": "2.1.2", 37 | "@types/node": "20.17.10", 38 | "@types/react": "18.3.17", 39 | "@types/react-dom": "18.3.5", 40 | "@types/react-helmet": "^6.1.11", 41 | "cross-env": "^7.0.3", 42 | "eslint": "9.17.0", 43 | "eslint-config-next": "15.1.0", 44 | "eslint-plugin-jsx-a11y": "6.10.2", 45 | "eslint-plugin-react": "7.37.2", 46 | "eslint-plugin-react-hooks": "5.1.0", 47 | "husky": "^8.0.3", 48 | "lint-staged": "^13.3.0", 49 | "prettier": "^3.5.3", 50 | "typescript": "^5.8.3", 51 | "typescript-eslint": "8.18.1" 52 | }, 53 | "keywords": [ 54 | "react-hook-form", 55 | "form-validation" 56 | ], 57 | "license": "MIT", 58 | "scripts": { 59 | "analyze": "cross-env ANALYZE=true next build", 60 | "build": "next build", 61 | "dev": "next dev", 62 | "format": "prettier . --check", 63 | "format:fix": "prettier . --write", 64 | "lint": "next lint", 65 | "lint:fix": "next lint --fix", 66 | "lint:ci": "next build --no-lint && next lint", 67 | "now-build": "pnpm run build", 68 | "start": "next start", 69 | "typecheck": "tsc --noEmit", 70 | "typecheck:ci": "next build --no-lint && tsc --noEmit", 71 | "prepare": "husky install" 72 | }, 73 | "lint-staged": { 74 | "*.{ts,tsx,mdx,css,json}": [ 75 | "pnpm run format:fix" 76 | ] 77 | }, 78 | "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c", 79 | "pnpm": { 80 | "overrides": { 81 | "@types/react": "18.3.17", 82 | "@types/react-dom": "18.3.5" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /public/images/Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logo 5 | Created with Sketch. 6 | 7 | 8 | 20 | 21 | -------------------------------------------------------------------------------- /public/images/casinoreviews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/casinoreviews.png -------------------------------------------------------------------------------- /public/images/dev-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/dev-tool.png -------------------------------------------------------------------------------- /public/images/follower24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/follower24.png -------------------------------------------------------------------------------- /public/images/formik.min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/formik.min.png -------------------------------------------------------------------------------- /public/images/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/gatsby-icon.png -------------------------------------------------------------------------------- /public/images/hookForm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/hookForm.png -------------------------------------------------------------------------------- /public/images/hookform.min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/hookform.min.png -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/logo/react-hook-form-logo-grey.png -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo-only-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/logo/react-hook-form-logo-only-grey.png -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo-only-grey.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/logo/react-hook-form-logo-only.png -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo-only.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/logo/react-hook-form-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/logo/react-hook-form-logo.png -------------------------------------------------------------------------------- /public/images/open-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/react-hook-form-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/react-hook-form-og.png -------------------------------------------------------------------------------- /public/images/reduxform.min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/reduxform.min.png -------------------------------------------------------------------------------- /public/images/route4me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/route4me.png -------------------------------------------------------------------------------- /public/images/sanity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/sanity.png -------------------------------------------------------------------------------- /public/images/twicsy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-hook-form/documentation/9e17128086b6422e6ecaaa6d94b87b9e83161448/public/images/twicsy.png -------------------------------------------------------------------------------- /src/components/Admonition.module.css: -------------------------------------------------------------------------------- 1 | .admonition { 2 | margin-top: 1rem; 3 | margin-bottom: 1em; 4 | padding: 15px 30px 15px 15px; 5 | border-radius: 0.4rem; 6 | } 7 | 8 | .admonitionIcon { 9 | display: inline-block; 10 | vertical-align: middle; 11 | margin-right: 0.2em; 12 | } 13 | 14 | .admonitionIcon svg { 15 | display: inline-block; 16 | width: 22px; 17 | height: 22px; 18 | stroke-width: 0; 19 | } 20 | 21 | .admonitionIcon svg { 22 | stroke: var(--color-black); 23 | fill: var(--color-black); 24 | } 25 | 26 | :global(.dark) .admonitionIcon svg { 27 | stroke: rgb(253, 253, 254); 28 | fill: rgb(253, 253, 254); 29 | } 30 | 31 | .admonitionContent > :last-child { 32 | margin-bottom: 0; 33 | } 34 | 35 | /** Customization */ 36 | .admonitionWarning { 37 | background-color: rgba(230, 126, 34, 0.1); 38 | border-left: 8px solid var(--color-orange); 39 | } 40 | 41 | .admonitionTip { 42 | background-color: rgba(46, 204, 113, 0.1); 43 | border-left: 8px solid var(--color-green); 44 | } 45 | 46 | .admonitionCaution { 47 | background-color: rgba(231, 76, 60, 0.1); 48 | border-left: 8px solid var(--color-secondary); 49 | } 50 | 51 | .admonitionImportant { 52 | background-color: rgb(25, 60, 71, 0.1); 53 | border-left: 8px solid var(--color-blue); 54 | } 55 | 56 | .admonitionNote { 57 | background-color: rgb(71, 71, 72, 0.1); 58 | border-left: 8px solid var(--color-text); 59 | } 60 | 61 | .admonitionQuestion { 62 | background-color: rgba(8, 61, 119, 0.1); 63 | border-left: 8px solid var(--color-light-blue); 64 | } 65 | -------------------------------------------------------------------------------- /src/components/ApiFormState.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react" 2 | import API from "../data/api" 3 | import typographyStyles from "../styles/typography.module.css" 4 | import FormStateTable from "./FormStateTable" 5 | import TabGroup from "./TabGroup" 6 | import CodeArea from "./CodeArea" 7 | import formStateUseEffect from "./codeExamples/formStateUseEffect" 8 | import formStateUseEffectTs from "./codeExamples/formStateUseEffectTs" 9 | 10 | function ApiFormState({ api }: { api: typeof API }) { 11 | return ( 12 | <> 13 | 14 |

15 | formState: Object 16 |

17 |
18 | {api.formState.description} 19 | 20 | 21 | 22 |

23 | Rules 24 |

25 | 26 |
    27 |
  • 28 |

    29 | formState is wrapped with a{" "} 30 | 35 | Proxy 36 | {" "} 37 | to improve render performance and skip extra logic if specific state 38 | is not subscribed to. Therefore make sure you invoke or read it 39 | before a render in order to enable the state update. 40 |

    41 |
  • 42 |
  • 43 |

    44 | formState is updated in batch. If you want to subscribe 45 | to formState via useEffect, make sure that 46 | you place the entire formState in the optional array. 47 |

    48 | 49 | { 51 | if (formState.errors.firstName) { 52 | // do the your logic here 53 | } 54 | }, [formState]); // ✅ 55 | // ❌ formState.errors will not trigger the useEffect 56 | `} 57 | /> 58 | 62 | 63 |
  • 64 |
  • 65 |

    66 | Pay attention to the logical operator when subscription to{" "} 67 | formState. 68 |

    69 | 70 | ; 74 | 75 | // ✅ read all formState values to subscribe to changes 76 | const { isDirty, isValid } = formState; 77 | return
  • 81 |
82 | 83 | ) 84 | } 85 | 86 | export default memo(ApiFormState) 87 | -------------------------------------------------------------------------------- /src/components/ApiGallery.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | max-width: 768px; 3 | margin: 60px auto 0; 4 | } 5 | 6 | @media (min-width: 768px) { 7 | .root { 8 | max-width: 840px; 9 | } 10 | } 11 | 12 | .gallery { 13 | display: grid; 14 | list-style: none; 15 | margin: 20px; 16 | grid-gap: 25px; 17 | grid-template-columns: 1fr; 18 | padding: 0; 19 | } 20 | 21 | @media (min-width: 768px) { 22 | .gallery { 23 | margin: 60px auto 60px; 24 | padding: 0 20px; 25 | grid-gap: 25px; 26 | grid-template-columns: repeat(3, 1fr); 27 | } 28 | } 29 | 30 | @media (min-width: 1024px) { 31 | .gallery { 32 | max-width: 1024px; 33 | } 34 | } 35 | 36 | .gallery li { 37 | border: 1px solid var(--color-light-blue); 38 | position: relative; 39 | padding-bottom: 30px; 40 | transition: transform 0.3s ease; 41 | } 42 | 43 | .gallery li:hover { 44 | border: 1px solid var(--color-secondary); 45 | outline-width: 0; 46 | box-shadow: 2px 2px 0 4px #000; 47 | transform: translate(-2px, -2px); 48 | } 49 | 50 | .gallery li a { 51 | position: absolute; 52 | right: 0; 53 | bottom: 0; 54 | top: 0; 55 | left: 0; 56 | display: flex; 57 | justify-content: flex-end; 58 | align-items: flex-end; 59 | padding: 12px 20px 12px 80px; 60 | font-size: 14px; 61 | } 62 | 63 | .gallery h3 { 64 | color: white; 65 | margin: 0; 66 | font-weight: normal; 67 | padding: 13px 20px; 68 | font-size: 18px; 69 | background: var(--color-button-blue); 70 | border-bottom: 1px solid var(--color-light-blue); 71 | letter-spacing: 1px; 72 | } 73 | 74 | .gallery h3 code { 75 | margin-right: 5px; 76 | letter-spacing: -1px; 77 | } 78 | 79 | .gallery p { 80 | margin: 20px; 81 | font-size: 14px; 82 | } 83 | 84 | .versionControl { 85 | text-align: right; 86 | padding-right: 20px; 87 | } 88 | 89 | .versionControl > div { 90 | display: inline-block; 91 | } 92 | 93 | .versionControl > p { 94 | font-size: 14px; 95 | color: var(--color-light-grey); 96 | display: inline-block; 97 | margin-right: 20px; 98 | } 99 | 100 | p.beta { 101 | margin-bottom: -1rem; 102 | font-size: 0.7rem; 103 | color: var(--color-light-grey); 104 | } 105 | -------------------------------------------------------------------------------- /src/components/ApiPage.module.css: -------------------------------------------------------------------------------- 1 | .mobileTypeText { 2 | font-weight: 400; 3 | font-size: 15px; 4 | font-family: monospace; 5 | font-variant-ligatures: none; 6 | color: var(--color-light-pink); 7 | margin-top: 10px; 8 | display: block; 9 | } 10 | 11 | .quickSelect { 12 | position: relative; 13 | max-width: 320px; 14 | margin: 0 auto; 15 | } 16 | 17 | .quickSelect:after { 18 | content: "▼"; 19 | font-size: 15px; 20 | right: 17%; 21 | top: 12px; 22 | position: absolute; 23 | pointer-events: none; 24 | } 25 | 26 | .quickSelect > select { 27 | border-radius: 4px; 28 | border: 1px solid var(--color-light-blue); 29 | appearance: none; 30 | background: none; 31 | color: white; 32 | margin: 0.67em auto 20px; 33 | display: block; 34 | text-align: center; 35 | text-align-last: center; 36 | font-size: 2rem; 37 | font-weight: lighter; 38 | position: relative; 39 | padding: 10px 30px; 40 | max-width: 240px; 41 | } 42 | 43 | .versionToggle { 44 | position: absolute; 45 | right: 20px; 46 | } 47 | 48 | @media (min-width: 768px) { 49 | .hiddenMenu > h1 { 50 | display: block; 51 | } 52 | 53 | .hiddenMenu > div { 54 | display: none; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Bday.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: none; 3 | } 4 | 5 | .root h1 > span { 6 | font-size: 30px; 7 | display: inline; 8 | font-weight: 400; 9 | margin-left: -15px; 10 | } 11 | 12 | @media (min-width: 768px) { 13 | .root { 14 | width: 630px; 15 | margin: 0 auto 0px; 16 | align-items: inherit; 17 | grid-template-areas: "d e"; 18 | border: 1px solid var(--color-light-blue); 19 | padding: 0 40px 40px; 20 | border-radius: 7px; 21 | box-shadow: 0 0 20px var(--color-primary); 22 | display: grid; 23 | } 24 | } 25 | 26 | .root span { 27 | display: block; 28 | } 29 | 30 | .happy { 31 | align-self: center; 32 | grid-area: a; 33 | text-align: right; 34 | font-size: 50px; 35 | font-weight: 600; 36 | color: var(--color-light-pink); 37 | } 38 | 39 | .birthday { 40 | align-self: center; 41 | grid-area: c; 42 | text-align: right; 43 | font-size: 50px; 44 | font-weight: 400; 45 | position: relative; 46 | margin-top: -10px; 47 | } 48 | 49 | .birthday p { 50 | bottom: -35px; 51 | right: 0; 52 | position: absolute; 53 | font-size: 12px; 54 | margin: 0; 55 | color: var(--color-light-blue); 56 | padding: 0; 57 | } 58 | 59 | .first { 60 | margin-top: 0; 61 | font-size: 130px; 62 | line-height: 120px; 63 | font-weight: bold; 64 | padding-left: 10px; 65 | display: block; 66 | grid-area: b; 67 | } 68 | 69 | .first sup { 70 | font-size: 30px; 71 | font-weight: 400; 72 | top: -4.1rem; 73 | } 74 | 75 | .cake { 76 | grid-area: d; 77 | text-align: center; 78 | font-size: 80px; 79 | line-height: 100px; 80 | } 81 | 82 | .cake svg { 83 | fill: white; 84 | height: 180px; 85 | } 86 | 87 | .achievement { 88 | grid-area: e; 89 | align-self: end; 90 | } 91 | 92 | .achievementList { 93 | display: grid; 94 | padding-left: 10px; 95 | } 96 | 97 | .achievementList h4 { 98 | margin-left: 12px; 99 | font-size: 30px; 100 | font-weight: 400; 101 | margin-bottom: 20px; 102 | margin-top: 40px; 103 | } 104 | 105 | .achievementList h4 span { 106 | border-bottom: 1px solid var(--color-light-blue); 107 | padding-bottom: 5px; 108 | display: inline-block; 109 | } 110 | 111 | .achievementList ul { 112 | list-style: none; 113 | margin: 0; 114 | } 115 | 116 | .achievementList p { 117 | padding: 5px 0; 118 | margin: 0; 119 | font-size: 14px; 120 | } 121 | 122 | .achievement strong { 123 | color: var(--color-light-pink); 124 | font-weight: 500; 125 | } 126 | 127 | .link { 128 | border: 1px solid white; 129 | margin-left: 40px; 130 | text-decoration: none; 131 | text-align: center; 132 | padding: 5px 0; 133 | margin-top: 20px; 134 | display: inline-block; 135 | border-radius: 25px; 136 | font-size: 14px; 137 | color: white; 138 | width: 150px; 139 | } 140 | 141 | .link:hover { 142 | background-color: white; 143 | color: var(--color-primary); 144 | border: 1px solid var(--color-primary); 145 | } 146 | 147 | .avatars { 148 | margin-right: 0; 149 | display: grid; 150 | grid-template-columns: 1fr 1fr 1fr 1fr; 151 | } 152 | 153 | .avatars img { 154 | height: 60px; 155 | margin: 2px; 156 | } 157 | -------------------------------------------------------------------------------- /src/components/BeekaiBuilderPage.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background: #0f111b; 3 | border-top: 1px solid #1e2a4a; 4 | border-bottom: 1px solid #1e2a4a; 5 | padding: 20px; 6 | } 7 | 8 | .logo { 9 | height: 25px; 10 | width: 100px; 11 | } 12 | 13 | .container { 14 | max-width: 910px; 15 | margin: 40px auto 20px; 16 | display: flex; 17 | padding: 0 20px; 18 | flex-direction: column; 19 | } 20 | 21 | .container > div { 22 | flex: 1; 23 | } 24 | 25 | .container a { 26 | width: 100%; 27 | text-align: center; 28 | text-decoration: none; 29 | margin-top: 20px; 30 | } 31 | 32 | .heading { 33 | font-size: 1.5rem; 34 | line-height: 1.3; 35 | font-weight: 600; 36 | margin-top: 20px; 37 | } 38 | 39 | .features { 40 | margin-top: 30px; 41 | } 42 | 43 | .product { 44 | position: relative; 45 | } 46 | 47 | .productImg { 48 | background: url("https://www.beekai.com/marketing/features/form/form-builder-hero.png") 49 | no-repeat; 50 | border-radius: 10px; 51 | display: block; 52 | background-size: 800px; 53 | background-position-x: -40px; 54 | max-height: 550px; 55 | min-height: 200px; 56 | z-index: 1; 57 | position: relative; 58 | } 59 | 60 | .product::after { 61 | content: ""; 62 | position: absolute; 63 | top: -0.5rem; 64 | bottom: -0.5rem; 65 | left: -0.5rem; 66 | right: -0.5rem; 67 | filter: blur(3rem); 68 | transform: scale(0.96); 69 | background-image: linear-gradient(230deg, #b367fe, #ec5990, #bf1650); 70 | opacity: 1; 71 | transition: 0.3s; 72 | animation: scale 3s alternate-reverse ease infinite; 73 | } 74 | 75 | .content { 76 | z-index: 1; 77 | } 78 | 79 | @keyframes scale { 80 | 0% { 81 | transform: scale(0.9); 82 | } 83 | 50% { 84 | transform: scale(0.85); 85 | } 86 | 100% { 87 | transform: scale(0.88); 88 | } 89 | } 90 | 91 | @media (min-width: 768px) { 92 | .container { 93 | flex-direction: row; 94 | gap: 40px; 95 | } 96 | 97 | .heading { 98 | font-size: 1.8rem; 99 | font-weight: 700; 100 | margin-top: 0; 101 | } 102 | 103 | .productImg { 104 | background-size: 700px; 105 | height: 100%; 106 | background-position-x: -35px; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/components/BeekaiBuilderPage.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./BeekaiBuilderPage.module.css" 2 | import buttonStyles from "../styles/button.module.css" 3 | 4 | export function BeekaiBuilderPage() { 5 | return ( 6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 | BEEKAI Form builder 18 |

Next-gen form builder

19 |

20 | Build the next-generation forms with modern technology and best in 21 | class user experience and accessibility. 22 |

23 | 24 |
    25 |
  • 26 |

    Generate code for React/Vanilla JS

    27 |
  • 28 |
  • 29 |

    GUI with drag and drop

    30 |
  • 31 |
  • 32 |

    Improved accessibility by default

    33 |
  • 34 |
  • 35 |

    Support dynamic field array

    36 |
  • 37 |
  • 38 |

    End-to-end integration with submission

    39 |
  • 40 |
  • 41 |

    User behaviour analytic

    42 |
  • 43 |
  • 44 |

    and many more features

    45 |
  • 46 |
47 | 48 | 54 | Try it now 55 | 56 |
57 |
58 |
59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /src/components/BuilderPage.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100vh; 7 | z-index: 11; 8 | box-sizing: border-box; 9 | -webkit-overflow-scrolling: touch; 10 | } 11 | 12 | .builder { 13 | overflow: auto; 14 | height: 100vh; 15 | background: var(--color-background); 16 | } 17 | 18 | .pageWrapper { 19 | display: grid; 20 | grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); 21 | grid-column-gap: 60px; 22 | overflow: hidden; 23 | max-width: 2000px; 24 | margin: 0 auto 20px; 25 | padding: 0 20px 20px 20px; 26 | } 27 | 28 | .pageWrapper > section:first-child { 29 | margin-top: 50px; 30 | order: 3; 31 | } 32 | 33 | .pageWrapper > form:nth-child(2) { 34 | order: 1; 35 | } 36 | 37 | .pageWrapper > section:nth-child(3) { 38 | order: 2; 39 | } 40 | 41 | .form select, 42 | .form input { 43 | display: block; 44 | box-sizing: border-box; 45 | width: 100%; 46 | border-radius: 4px; 47 | padding: 6px 10px; 48 | margin-bottom: 10px; 49 | font-size: 16px; 50 | } 51 | 52 | .form select:hover, 53 | .form input:hover { 54 | border: 1px solid var(--color-light-pink); 55 | } 56 | 57 | .form select:not([multiple]) { 58 | height: 40px; 59 | } 60 | 61 | .form input.form-error { 62 | border: 1px solid #bf1650; 63 | } 64 | 65 | .form input[type="checkbox"] { 66 | display: inline-block; 67 | width: auto; 68 | margin-right: 10px; 69 | } 70 | 71 | .form label { 72 | line-height: 2; 73 | text-align: left; 74 | display: block; 75 | margin-bottom: 13px; 76 | margin-top: 20px; 77 | } 78 | 79 | .form fieldset { 80 | border: 1px solid var(--color-light-blue); 81 | border-radius: 4px; 82 | } 83 | 84 | .closeButton { 85 | font-size: 25px; 86 | position: absolute; 87 | cursor: pointer; 88 | z-index: 5; 89 | border-radius: 4px; 90 | color: white; 91 | top: 20px; 92 | right: 30px; 93 | width: 50px; 94 | height: 50px; 95 | display: flex; 96 | justify-content: center; 97 | background: var(--color-primary); 98 | border: 1px solid white; 99 | } 100 | 101 | .closeButton:hover { 102 | border: 1px solid var(--color-secondary); 103 | } 104 | 105 | @media (min-width: 768px) { 106 | .pageWrapper > section:first-child { 107 | margin-top: 0; 108 | order: 1; 109 | } 110 | 111 | .pageWrapper > form:nth-child(2) { 112 | order: 2; 113 | } 114 | 115 | .pageWrapper > section:nth-child(3) { 116 | order: 3; 117 | } 118 | 119 | .closeButton { 120 | align-items: center; 121 | justify-content: center; 122 | display: flex; 123 | } 124 | } 125 | 126 | .buttonWrapper { 127 | display: flex; 128 | position: absolute; 129 | top: 10px; 130 | right: 5px; 131 | } 132 | 133 | .button { 134 | border: none; 135 | color: white; 136 | border-radius: 0; 137 | font-size: 13px; 138 | padding: 0 10px; 139 | right: 20px; 140 | z-index: 1; 141 | top: 10px; 142 | display: none; 143 | cursor: pointer; 144 | text-transform: uppercase; 145 | height: 34px; 146 | align-items: center; 147 | margin: 0 3px; 148 | } 149 | 150 | .button:hover { 151 | background: var(--color-secondary); 152 | color: white; 153 | } 154 | 155 | @media (min-width: 768px) { 156 | .button { 157 | display: flex; 158 | } 159 | } 160 | 161 | .copyButton { 162 | background: var(--color-light-blue); 163 | border: 1px solid transparent; 164 | } 165 | 166 | .active, 167 | .copyButton:hover { 168 | background: none; 169 | border: 1px solid var(--color-secondary); 170 | color: white; 171 | } 172 | 173 | .active, 174 | .copyButton:hover span { 175 | background: var(--color-primary); 176 | } 177 | 178 | .wrapper pre { 179 | line-height: 1.5 !important; 180 | } 181 | 182 | .wrapper pre code { 183 | display: none; 184 | } 185 | 186 | .wrapper pre code.showCode { 187 | display: block; 188 | } 189 | -------------------------------------------------------------------------------- /src/components/ClipBoard.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import generic from "../data/generic" 3 | 4 | const ClipBoard = ({ 5 | className, 6 | onClick, 7 | }: { 8 | onClick: () => void 9 | className?: string 10 | }) => { 11 | const [copiedCode, setCopiedCode] = useState(false) 12 | 13 | useEffect(() => { 14 | if (!copiedCode) { 15 | return 16 | } 17 | 18 | const timerId = setTimeout(() => { 19 | setCopiedCode(false) 20 | }, 3000) 21 | 22 | return () => { 23 | clearTimeout(timerId) 24 | } 25 | }, [copiedCode]) 26 | 27 | return ( 28 | 38 | ) 39 | } 40 | 41 | export default ClipBoard 42 | -------------------------------------------------------------------------------- /src/components/CodeArea.module.css: -------------------------------------------------------------------------------- 1 | .buttonWrapper { 2 | display: flex; 3 | position: absolute; 4 | top: 10px; 5 | right: 5px; 6 | } 7 | 8 | .button { 9 | border: none; 10 | color: white; 11 | border-radius: 0; 12 | font-size: 13px; 13 | padding: 0 10px; 14 | right: 20px; 15 | z-index: 1; 16 | top: 10px; 17 | display: none; 18 | cursor: pointer; 19 | text-transform: uppercase; 20 | height: 34px; 21 | align-items: center; 22 | margin: 0 3px; 23 | } 24 | 25 | .codeLink { 26 | background: var(--color-light-blue); 27 | border: 1px solid transparent; 28 | } 29 | 30 | .button:hover { 31 | background: var(--color-secondary); 32 | color: white; 33 | } 34 | 35 | @media (min-width: 768px) { 36 | .button { 37 | display: flex; 38 | } 39 | } 40 | 41 | .copyButton { 42 | background: none; 43 | border: 1px solid transparent; 44 | color: currentColor; 45 | } 46 | 47 | .active, 48 | .copyButton:hover { 49 | background: none; 50 | border: 1px solid var(--color-secondary); 51 | color: white; 52 | } 53 | 54 | .active, 55 | .copyButton:hover span { 56 | background: var(--color-primary); 57 | } 58 | 59 | .linkToSandBox { 60 | text-decoration: none; 61 | line-height: 2; 62 | right: 115px; 63 | color: inherit; 64 | } 65 | 66 | .linkToSandBox > svg { 67 | display: inline-block; 68 | height: 18px; 69 | position: relative; 70 | margin-right: 8px; 71 | } 72 | -------------------------------------------------------------------------------- /src/components/CodeArea.tsx: -------------------------------------------------------------------------------- 1 | import copyClipBoard from "./utils/copyClipBoard" 2 | import ClipBoard from "./ClipBoard" 3 | import styles from "./CodeArea.module.css" 4 | import { useRef, useState } from "react" 5 | import { CodeSandBoxLink } from "./CodeSandbox" 6 | import { PrismSyntaxHighlight } from "./mdx/code" 7 | 8 | const ToggleTypes = { 9 | js: "JS", 10 | ts: "TS", 11 | types: "TYPES", 12 | } as const 13 | 14 | export default function CodeArea({ 15 | rawData, 16 | tsRawData, 17 | rawTypes, 18 | url, 19 | tsUrl, 20 | withOutCopy, 21 | isExpo, 22 | }: { 23 | rawData?: string 24 | tsRawData?: string 25 | rawTypes?: string 26 | url?: string 27 | tsUrl?: string 28 | withOutCopy?: boolean 29 | isExpo?: boolean 30 | }) { 31 | const [currentType, setType] = useState< 32 | (typeof ToggleTypes)[keyof typeof ToggleTypes] 33 | >(() => { 34 | if (rawData) return ToggleTypes.js 35 | if (tsRawData) return ToggleTypes.ts 36 | return ToggleTypes.types 37 | }) 38 | 39 | const codeAreaRef = useRef(null) 40 | 41 | return ( 42 |
47 |
48 | {((rawData && tsRawData) || (rawData && rawTypes)) && ( 49 | 59 | )} 60 | {((tsRawData && rawData) || (tsRawData && rawTypes)) && ( 61 | 71 | )} 72 | {((rawTypes && rawData) || (rawTypes && tsRawData)) && ( 73 | 83 | )} 84 | {!withOutCopy && ( 85 | { 88 | copyClipBoard(codeAreaRef.current?.innerText || "") 89 | }} 90 | /> 91 | )} 92 | 93 | {((url && currentType === ToggleTypes.js) || 94 | (tsUrl && currentType === ToggleTypes.ts) || 95 | (tsUrl && currentType === ToggleTypes.types)) && ( 96 | 101 | )} 102 |
103 | 104 |
105 | {currentType === ToggleTypes.js && ( 106 | 107 | {rawData} 108 | 109 | )} 110 | {currentType === ToggleTypes.ts && ( 111 | 112 | {tsRawData} 113 | 114 | )} 115 | {currentType === ToggleTypes.types && ( 116 | 117 | {rawTypes} 118 | 119 | )} 120 |
121 |
122 | ) 123 | } 124 | -------------------------------------------------------------------------------- /src/components/CodeCompareSection.module.css: -------------------------------------------------------------------------------- 1 | .gridView { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .gridView > div:first-child { 7 | order: 1; 8 | } 9 | 10 | .gridView iframe { 11 | display: none; 12 | box-shadow: 0 0 20px #010817; 13 | } 14 | 15 | .fullScreen { 16 | background: none; 17 | color: white; 18 | position: absolute; 19 | z-index: 1; 20 | right: 0; 21 | font-size: 12px; 22 | border-top: none; 23 | border-right: none; 24 | border-color: var(--color-secondary); 25 | border-bottom-left-radius: 4px; 26 | display: none; 27 | } 28 | 29 | .fullScreen:hover { 30 | background: var(--color-light-pink); 31 | } 32 | 33 | @media (min-width: 1000px) { 34 | .gridView { 35 | display: grid; 36 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 37 | grid-column-gap: 40px; 38 | max-width: 1024px; 39 | margin: 0 auto; 40 | } 41 | 42 | .gridView iframe { 43 | display: block; 44 | } 45 | 46 | .gridView > div:first-child { 47 | order: 0; 48 | } 49 | 50 | .fullScreen { 51 | display: block; 52 | } 53 | 54 | .display { 55 | display: none; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/CodeCompareSection.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react" 2 | import reactHookFormCode from "./codeExamples/reactHookFormCode" 3 | import CodeArea from "./CodeArea" 4 | import { AnimateGroup, Animate } from "react-simple-animate" 5 | import home from "../data/home" 6 | import typographyStyles from "../styles/typography.module.css" 7 | import containerStyles from "../styles/container.module.css" 8 | import styles from "./CodeCompareSection.module.css" 9 | 10 | const props = { 11 | start: { transform: "translateY(100px)" }, 12 | end: { transform: "translateY(0)" }, 13 | easeType: "ease-in", 14 | } 15 | 16 | function CodeCompareSection({ 17 | isPlayCodeCompare, 18 | }: { 19 | isPlayCodeCompare: boolean 20 | }) { 21 | return ( 22 | 23 |
28 |
29 |

{home.codeComparison.title}

30 | 31 | {home.codeComparison.description} 32 |
33 | 34 |
40 | {isPlayCodeCompare && ( 41 |