├── .vscode
└── settings.json
├── packages
├── react
│ ├── src
│ │ ├── use-form
│ │ │ ├── index.ts
│ │ │ ├── fields-utils.ts
│ │ │ ├── types.ts
│ │ │ └── use-form.test.tsx
│ │ ├── index.ts
│ │ ├── create-form
│ │ │ ├── index.ts
│ │ │ ├── exception.ts
│ │ │ ├── fields-utils.ts
│ │ │ ├── debounce.ts
│ │ │ ├── store.test.ts
│ │ │ ├── store.ts
│ │ │ └── types.ts
│ │ └── types.ts
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── jest.config.js
│ └── package.json
├── object-utils
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── jest.config.js
│ ├── CHANGELOG.md
│ ├── package.json
│ └── src
│ │ ├── index.test.ts
│ │ └── index.ts
├── validation
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── jest.config.js
│ ├── CHANGELOG.md
│ ├── package.json
│ └── src
│ │ ├── index.ts
│ │ └── index.test.ts
├── tsconfig
│ ├── package.json
│ ├── node16.json
│ ├── react-library.json
│ ├── vite.json
│ ├── base.json
│ ├── next.json
│ └── CHANGELOG.md
└── eslint-config
│ ├── index.js
│ ├── package.json
│ └── CHANGELOG.md
├── img
├── media.png
├── createform-flow.png
└── logo.svg
├── pnpm-workspace.yaml
├── docs
├── public
│ ├── favicon.ico
│ ├── favicon.png
│ └── images
│ │ ├── createform-flow.png
│ │ └── logo.svg
├── .gitignore
├── app
│ ├── routes.ts
│ ├── routes
│ │ ├── components
│ │ │ ├── index.tsx
│ │ │ ├── icon.tsx
│ │ │ ├── pulse.tsx
│ │ │ └── hero-content.tsx
│ │ └── home.tsx
│ ├── docs
│ │ ├── search.ts
│ │ ├── mdx-components.tsx
│ │ └── page.tsx
│ ├── demos
│ │ ├── demo.tsx
│ │ ├── native-demo.tsx
│ │ ├── custom-demo.tsx
│ │ ├── inline-validation-demo.tsx
│ │ ├── on-submit-demo.tsx
│ │ ├── debounced-demo.tsx
│ │ └── on-change-demo.tsx
│ ├── mdx-components.tsx
│ ├── source.ts
│ ├── app.css
│ └── root.tsx
├── react-router.config.ts
├── content
│ └── docs
│ │ ├── meta.json
│ │ ├── useform
│ │ ├── validation.mdx
│ │ └── creating-a-form.mdx
│ │ ├── createform
│ │ ├── form-submission.mdx
│ │ ├── async-data.mdx
│ │ ├── using-your-form
│ │ │ ├── native-elements.mdx
│ │ │ └── custom-components.mdx
│ │ ├── validation
│ │ │ └── inline-validation.mdx
│ │ ├── creating-forms
│ │ │ ├── onchange-form.mdx
│ │ │ └── debounced-form.mdx
│ │ ├── testing
│ │ │ ├── testing-on-change.mdx
│ │ │ └── testing-on-submit.mdx
│ │ └── how-it-works.mdx
│ │ ├── about.md
│ │ ├── index.mdx
│ │ ├── migration.mdx
│ │ └── api-reference
│ │ └── use-form.mdx
├── vite.config.ts
├── tsconfig.json
├── package.json
└── CHANGELOG.md
├── examples
├── public
│ └── favicon.ico
├── tsconfig.json
├── src
│ ├── main.tsx
│ ├── App.tsx
│ └── Examples
│ │ ├── createForm
│ │ ├── MultiStep
│ │ │ ├── use-person-form.ts
│ │ │ ├── Form.tsx
│ │ │ ├── address-step.tsx
│ │ │ └── basic-info-step.tsx
│ │ ├── MultiStepWithYup
│ │ │ ├── Form.tsx
│ │ │ ├── use-person-form.ts
│ │ │ ├── basic-info-step.tsx
│ │ │ └── address-step.tsx
│ │ ├── yup-validation.tsx
│ │ ├── zod-validation.tsx
│ │ ├── radio-and-checkbox.tsx
│ │ ├── yup-inline-validation.tsx
│ │ ├── zod-inline-validation.tsx
│ │ └── form-example.tsx
│ │ └── useForm
│ │ └── form-data.tsx
├── .eslintrc.json
├── index.html
├── vite.config.ts
├── package.json
└── project.json
├── .husky
├── commit-msg
└── prepare-commit-msg
├── .gitignore
├── version.config.json
├── .eslintrc.js
├── turbo.json
├── .all-contributorsrc
├── package.json
├── CONTRIBUTING.md
├── PULL_REQUEST_TEMPLATE.md.txt
├── LICENCE
├── .github
└── workflows
│ └── release.yml
└── code-of-conduct.md
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "codeium.enableSearch": true
3 | }
4 |
--------------------------------------------------------------------------------
/packages/react/src/use-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./use-form";
2 |
--------------------------------------------------------------------------------
/img/media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/img/media.png
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "docs"
3 | - "examples"
4 | - "packages/*"
5 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./create-form";
2 | export * from "./use-form";
3 |
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/docs/public/favicon.png
--------------------------------------------------------------------------------
/img/createform-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/img/createform-flow.png
--------------------------------------------------------------------------------
/packages/react/src/create-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./create-form";
2 | export * from "./types";
3 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 |
4 | # React Router
5 | /.react-router/
6 | /build/
7 |
--------------------------------------------------------------------------------
/examples/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/examples/public/favicon.ico
--------------------------------------------------------------------------------
/packages/react/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ["createform"],
4 | };
5 |
--------------------------------------------------------------------------------
/packages/object-utils/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ["createform"],
4 | };
5 |
--------------------------------------------------------------------------------
/packages/validation/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ["createform"],
4 | };
5 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/docs/public/images/createform-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jucian0/createform/HEAD/docs/public/images/createform-flow.png
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@createform/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["dist", "build", "node_modules"]
5 | }
--------------------------------------------------------------------------------
/packages/object-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@createform/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["dist", "build", "node_modules"]
5 | }
--------------------------------------------------------------------------------
/packages/validation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@createform/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["dist", "build", "node_modules"]
5 | }
--------------------------------------------------------------------------------
/packages/tsconfig/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@createform/tsconfig",
3 | "version": "4.0.7",
4 | "license": "MIT",
5 | "publishConfig": {
6 | "access": "public"
7 | }
8 | }
--------------------------------------------------------------------------------
/.husky/prepare-commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | #exec < /dev/tty && npx cz --hook || true
5 |
6 | #unfortunally this hook can't work with semver library
7 |
--------------------------------------------------------------------------------
/examples/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react"
4 | },
5 | "extends": "@createform/tsconfig/vite.json",
6 | "include": ["."],
7 | "exclude": ["dist", "build", "node_modules"]
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .turbo
4 | *.log
5 | .next
6 | dist
7 | dist-ssr
8 | *.local
9 | .env
10 | .cache
11 | server/dist
12 | public/dist
13 | storybook-static/
14 | .npmrc
15 | .netrc
16 | pnpm-lock.yaml
--------------------------------------------------------------------------------
/examples/src/main.tsx:
--------------------------------------------------------------------------------
1 | import * as ReactDOM from "react-dom/client";
2 | import { App } from "./app";
3 |
4 | const root = ReactDOM.createRoot(
5 | document.getElementById("root") as HTMLElement
6 | );
7 | root.render();
8 |
--------------------------------------------------------------------------------
/version.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@turbo-version/version/turbov.schema.json",
3 | "tagPrefix": "v",
4 | "preset": "angular",
5 | "synced": true,
6 | "updateInternalDependencies": "patch"
7 | }
--------------------------------------------------------------------------------
/docs/app/routes.ts:
--------------------------------------------------------------------------------
1 | import { type RouteConfig, index, route } from '@react-router/dev/routes';
2 |
3 | export default [
4 | index('routes/home.tsx'),
5 | route('docs/*', 'docs/page.tsx'),
6 | route('api/search', 'docs/search.ts'),
7 | ] satisfies RouteConfig;
8 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | // This tells ESLint to load the config from the package `eslint-config-createform`
4 | extends: ["createform"],
5 | settings: {
6 | next: {
7 | rootDir: ["apps/*/"],
8 | },
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/object-utils/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "ts-jest",
3 | testEnvironment: "jsdom",
4 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
5 | moduleNameMapper: {
6 | "\\.(css|less)$": "identity-obj-proxy",
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/packages/validation/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "ts-jest",
3 | testEnvironment: "jsdom",
4 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
5 | moduleNameMapper: {
6 | "\\.(css|less)$": "identity-obj-proxy",
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/docs/app/routes/components/index.tsx:
--------------------------------------------------------------------------------
1 | import HeroContent from "./hero-content";
2 | import Highlights from "./highlights";
3 |
4 | export default function HomeContent() {
5 | return (
6 | <>
7 |
8 |
9 | >
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tsconfig/node16.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Node 16",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "lib": ["ES2020"],
7 | "module": "commonjs",
8 | "target": "ES2020"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/react-router.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from '@react-router/dev/config';
2 | import { source } from './app/source';
3 |
4 | export default {
5 | ssr: true,
6 | async prerender() {
7 | return [...source.getPages().map((page) => page.url)];
8 | },
9 | } satisfies Config;
10 |
--------------------------------------------------------------------------------
/packages/eslint-config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["next", "turbo", "prettier"],
3 | rules: {
4 | "@next/next/no-html-link-for-pages": "off",
5 | },
6 | parserOptions: {
7 | babelOptions: {
8 | presets: [require.resolve("next/babel")],
9 | },
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/packages/react/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'jsdom',
4 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
5 | moduleNameMapper: {
6 | '\\.(css|less)$': 'identity-obj-proxy'
7 | },
8 | setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect']
9 | };
--------------------------------------------------------------------------------
/docs/content/docs/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "pages": [
4 | "index",
5 | "quick-start",
6 | "best-practices",
7 | "migration",
8 | "api-reference",
9 | "createform",
10 | "useform",
11 | "tutorials",
12 | "examples",
13 | "troubleshooting",
14 | "about"
15 | ],
16 | "defaultOpened": true
17 | }
--------------------------------------------------------------------------------
/packages/tsconfig/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx",
7 | "lib": [
8 | "dom",
9 | "ES2015"
10 | ],
11 | "module": "ESNext",
12 | "target": "es6"
13 | }
14 | }
--------------------------------------------------------------------------------
/docs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { reactRouter } from '@react-router/dev/vite';
2 | import tailwindcss from '@tailwindcss/vite';
3 | import { defineConfig } from 'vite';
4 | import tsconfigPaths from 'vite-tsconfig-paths';
5 |
6 | export default defineConfig({
7 | build: {
8 | rollupOptions: {
9 | external: ['shiki'],
10 | },
11 | },
12 | plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
13 | });
14 |
--------------------------------------------------------------------------------
/examples/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/eslint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-createform",
3 | "version": "4.0.7",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "dependencies": {
7 | "eslint-config-next": "13.3.0",
8 | "eslint-config-prettier": "8.3.0",
9 | "eslint-plugin-react": "7.29.4",
10 | "eslint-config-turbo": "1.9.0",
11 | "eslint": "7.23.0",
12 | "typescript": "4.9.3"
13 | },
14 | "publishConfig": {
15 | "access": "public"
16 | }
17 | }
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "outputs": ["dist/**", "storybook-static/**"],
6 | "dependsOn": ["^build"]
7 | },
8 | "test": {
9 | "outputs": ["coverage/**"],
10 | "dependsOn": []
11 | },
12 | "lint": {},
13 | "dev": {
14 | "cache": false,
15 | "persistent": true
16 | },
17 | "clean": {
18 | "cache": false
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { ChakraProvider } from "@chakra-ui/react";
2 | import "react-datepicker/dist/react-datepicker.css";
3 | import { FormExample } from "./Examples/createForm/form-example";
4 | import { FormDataWay } from "./Examples/useForm/form-data";
5 | import React from "react";
6 | import { FormZodValidation } from "./Examples/createForm/zod-validation";
7 | export function App() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/packages/react/src/use-form/fields-utils.ts:
--------------------------------------------------------------------------------
1 | export function isRadioOrCheckbox(element: any) {
2 | return element.type === "radio" || element.type === "checkbox";
3 | }
4 |
5 | export function isSelect(element: any) {
6 | return element.type === "select";
7 | }
8 |
9 | export function setOptionAsDefault(element: HTMLSelectElement, value: any) {
10 | const index = Array.from(element.options).findIndex(
11 | (option) => option.value === value
12 | );
13 |
14 | if (index !== -1) {
15 | element.options[index].defaultSelected = true;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tsconfig/vite.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./base.json",
3 | "compilerOptions": {
4 | "target": "ESNext",
5 | "useDefineForClassFields": true,
6 | "module": "ESNext",
7 | "lib": [
8 | "ESNext",
9 | "DOM"
10 | ],
11 | "sourceMap": true,
12 | "resolveJsonModule": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true
17 | },
18 | "exclude": [
19 | "node_modules"
20 | ]
21 | }
--------------------------------------------------------------------------------
/packages/tsconfig/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "composite": false,
6 | "declaration": true,
7 | "declarationMap": true,
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "inlineSources": false,
11 | "isolatedModules": true,
12 | "moduleResolution": "node",
13 | "noUnusedLocals": false,
14 | "noUnusedParameters": false,
15 | "preserveWatchOutput": true,
16 | "skipLibCheck": true,
17 | "strict": true
18 | },
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/docs/app/docs/search.ts:
--------------------------------------------------------------------------------
1 | import type { Route } from './+types/search';
2 | import { createSearchAPI } from 'fumadocs-core/search/server';
3 | import { source } from '@/source';
4 | import { structure } from 'fumadocs-core/mdx-plugins';
5 |
6 | const server = createSearchAPI('advanced', {
7 | indexes: source.getPages().map((page) => ({
8 | id: page.url,
9 | url: page.url,
10 | title: page.data.title ?? '',
11 | description: page.data.description,
12 | structuredData: structure(page.data.content),
13 | })),
14 | });
15 |
16 | export async function loader({ request }: Route.LoaderArgs) {
17 | return server.GET(request);
18 | }
19 |
--------------------------------------------------------------------------------
/examples/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from "vite";
3 | import react from "@vitejs/plugin-react";
4 | import viteTsConfigPaths from "vite-tsconfig-paths";
5 |
6 | export default defineConfig({
7 | server: {
8 | port: 4200,
9 | host: "localhost",
10 | },
11 | plugins: [
12 | react(),
13 | // viteTsConfigPaths({
14 | // root: "../../",
15 | // }),
16 | ],
17 |
18 | test: {
19 | globals: true,
20 | cache: {
21 | dir: "../../node_modules/.vitest",
22 | },
23 | environment: "jsdom",
24 | include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStep/use-person-form.ts:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 |
3 | export type Person = {
4 | firstName: string;
5 | lastName: string;
6 | age: number | null;
7 | date: string;
8 | profession: any;
9 | address: {
10 | street: string;
11 | city: string;
12 | zipCode: string;
13 | };
14 | };
15 |
16 | export const usePersonForm = createForm({
17 | initialValues: {
18 | firstName: "",
19 | lastName: "",
20 | date: "",
21 | age: null,
22 | profession: null,
23 | address: {
24 | street: "",
25 | city: "",
26 | zipCode: "",
27 | },
28 | },
29 | mode: "onChange",
30 | });
31 |
--------------------------------------------------------------------------------
/packages/tsconfig/next.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Next.js",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "target": "es5",
7 | "lib": ["dom", "dom.iterable", "esnext"],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": false,
11 | "forceConsistentCasingInFileNames": true,
12 | "noEmit": true,
13 | "incremental": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "preserve"
19 | },
20 | "include": ["src", "next-env.d.ts"],
21 | "exclude": ["node_modules"]
22 | }
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "**/*",
4 | "**/.server/**/*",
5 | "**/.client/**/*",
6 | ".react-router/types/**/*"
7 | ],
8 | "compilerOptions": {
9 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
10 | "types": ["node", "vite/client"],
11 | "target": "esnext",
12 | "module": "esnext",
13 | "moduleResolution": "bundler",
14 | "jsx": "react-jsx",
15 | "rootDirs": [".", "./.react-router/types"],
16 | "baseUrl": ".",
17 | "paths": {
18 | "@/*": ["./app/*"]
19 | },
20 | "esModuleInterop": true,
21 | "verbatimModuleSyntax": true,
22 | "noEmit": true,
23 | "resolveJsonModule": true,
24 | "skipLibCheck": true,
25 | "strict": true
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStepWithYup/Form.tsx:
--------------------------------------------------------------------------------
1 | import { Person, usePersonForm } from "./use-person-form";
2 | import { Wizard } from "react-use-wizard";
3 | import { BasicInfoStep } from "./basic-info-step";
4 | import { AddressStep } from "./address-step";
5 | import React from "react";
6 |
7 | export function MultiFormWithYup() {
8 | const form = usePersonForm();
9 |
10 | function handleSubmit(e: Person) {
11 | console.log(e);
12 | }
13 |
14 | function handleReset(e: Person) { }
15 |
16 | console.log(form.state.errors);
17 |
18 | return (
19 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/docs/app/routes/home.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from "./+types/home";
2 | import { HomeLayout } from "fumadocs-ui/layouts/home";
3 | import HomeContent from "./components";
4 |
5 | export function meta({ }: Route.MetaArgs) {
6 | return [
7 | { title: "CreateForm" },
8 | {
9 | name: "description",
10 | content:
11 | "The ReactJS form library",
12 | },
13 | ];
14 | }
15 |
16 | export default function Home() {
17 | return (
18 |
22 |
23 |
24 | ),
25 | }}
26 | >
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/docs/app/demos/demo.tsx:
--------------------------------------------------------------------------------
1 | const styles = {
2 | width: "100%",
3 | padding: "10px 0",
4 | display: "flex",
5 | justifyContent: "center",
6 | alignItems: "center",
7 | flexDirection: "column",
8 |
9 | h1: {
10 | fontSize: "3em",
11 | fontFamily: "'Roboto',sans-serif",
12 | color: "$primary",
13 | padding: "20px",
14 | },
15 |
16 | div: {
17 | width: "100%",
18 | padding: "2px",
19 | iframe: {
20 | width: "100%",
21 | height: 700,
22 | border: "none",
23 | borderRadius: "8px",
24 | boxShadow: "$sm",
25 | position: "relative",
26 | },
27 | },
28 | };
29 |
30 | export default function Demo(props) {
31 | return (
32 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/create-form/exception.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a base exception class for custom exceptions.
3 | *
4 | * @extends {Error}
5 | */
6 | export class Exception extends Error {
7 | /**
8 | * Creates a new instance of `Exception`.
9 | *
10 | * @param {string} message The error message.
11 | */
12 | constructor(message: string) {
13 | super(message);
14 | this.name = this.constructor.name;
15 | Error.captureStackTrace(this, this.constructor);
16 | }
17 | }
18 |
19 | /**
20 | * Represents an error that occurs when an invalid argument is passed to a function.
21 | *
22 | * @extends {Exception}
23 | */
24 | export class InvalidArgumentException extends Exception {
25 | /**
26 | * Creates a new instance of `InvalidArgumentException`.
27 | *
28 | * @param {string} message The error message.
29 | */
30 | constructor(message: string) {
31 | super(message);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "commitConvention": "angular",
8 | "contributors": [
9 | {
10 | "login": "nelsonuprety1",
11 | "name": "Nelson Uprety",
12 | "avatar_url": "https://avatars.githubusercontent.com/u/25173636?v=4",
13 | "profile": "https://nelsonuprety.netlify.app/",
14 | "contributions": [
15 | "code"
16 | ]
17 | },
18 | {
19 | "login": "jucian0",
20 | "name": "Juciano",
21 | "avatar_url": "https://avatars.githubusercontent.com/u/8399579?v=4",
22 | "profile": "http://juciano.com",
23 | "contributions": [
24 | "code"
25 | ]
26 | }
27 | ],
28 | "contributorsPerLine": 7,
29 | "skipCi": true,
30 | "repoType": "github",
31 | "repoHost": "https://github.com",
32 | "projectName": "createform",
33 | "projectOwner": "jucian0"
34 | }
35 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStep/Form.tsx:
--------------------------------------------------------------------------------
1 | import { Person, usePersonForm } from "./use-person-form";
2 | import { Wizard } from "react-use-wizard";
3 | import { BasicInfoStep } from "./basic-info-step";
4 | import { AddressStep } from "./address-step";
5 | import { Button } from "@chakra-ui/react";
6 | import React from "react";
7 |
8 | export function MultiForm() {
9 | const form = usePersonForm();
10 |
11 | function handleSubmit(e: Person) {
12 | console.log(e);
13 | }
14 |
15 | function handleReset(e: Person) { }
16 |
17 | console.log(form.state.errors);
18 |
19 | return (
20 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/docs/app/docs/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import defaultMdxComponents from 'fumadocs-ui/mdx';
2 | import * as TabsComponents from 'fumadocs-ui/components/tabs';
3 | import { OnSubmitFormDemo } from '@/demos/on-submit-demo';
4 | import { OnChangeFormDemo } from '@/demos/on-change-demo';
5 | import Demo from '@/demos/demo';
6 | import { NativeFormDemo } from '@/demos/native-demo';
7 | import { DebouncedFormDemo } from '@/demos/debounced-demo';
8 | import { CustomFormDemo } from '@/demos/custom-demo';
9 | import { InlineFormDemo } from '@/demos/inline-validation-demo';
10 |
11 | export function getMDXComponents(components?: any) {
12 | return {
13 | ...defaultMdxComponents,
14 | ...TabsComponents,
15 | ...components,
16 | InlineFormDemo: InlineFormDemo,
17 | CustomFormDemo: CustomFormDemo,
18 | DebouncedFormDemo: DebouncedFormDemo,
19 | NativeFormDemo: NativeFormDemo,
20 | Demo: Demo,
21 | OnChangeFormDemo: OnChangeFormDemo,
22 | OnSubmitFormDemo: OnSubmitFormDemo,
23 | };
24 | }
--------------------------------------------------------------------------------
/docs/app/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import defaultMdxComponents from 'fumadocs-ui/mdx';
2 | import * as TabsComponents from 'fumadocs-ui/components/tabs';
3 | import { InlineFormDemo } from './demos/inline-validation-demo';
4 | import { CustomFormDemo } from './demos/custom-demo';
5 | import { DebouncedFormDemo } from './demos/debounced-demo';
6 | import { NativeFormDemo } from './demos/native-demo';
7 | import Demo from './demos/demo';
8 | import { OnChangeFormDemo } from './demos/on-change-demo';
9 | import { OnSubmitFormDemo } from './demos/on-submit-demo';
10 |
11 | export function getMDXComponents(components?: any) {
12 | return {
13 | ...defaultMdxComponents,
14 | ...TabsComponents,
15 | ...components,
16 | // InlineFormDemo: InlineFormDemo,
17 | // CustomFormDemo: CustomFormDemo,
18 | // DebouncedFormDemo: DebouncedFormDemo,
19 | // NativeFormDemo: NativeFormDemo,
20 | // Demo: Demo,
21 | // OnChangeFormDemo: OnChangeFormDemo,
22 | // OnSubmitFormDemo: OnSubmitFormDemo,
23 | };
24 | }
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStep/address-step.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Input } from "@chakra-ui/react";
2 | import { Button, Stack, Text } from "@chakra-ui/react";
3 | import { usePersonForm } from "./use-person-form";
4 | import { useWizard } from "react-use-wizard";
5 | import React from "react";
6 |
7 | export function AddressStep() {
8 | const { previousStep } = useWizard();
9 | const { register } = usePersonForm();
10 |
11 | return (
12 |
13 | Address
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStepWithYup/use-person-form.ts:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import * as yup from "yup";
3 |
4 | export type Person = {
5 | firstName: string;
6 | lastName: string;
7 | age: number | null;
8 | address: {
9 | street: string;
10 | city: string;
11 | zipCode: string;
12 | };
13 | };
14 |
15 | const validationSchema = yup.object({
16 | firstName: yup.string().min(10).max(20),
17 | lastName: yup.string().min(10).max(20),
18 | age: yup.number().min(18),
19 | address: yup
20 | .object({
21 | street: yup.string().required(),
22 | city: yup.string().required(),
23 | zipCode: yup.string().required(),
24 | })
25 | .optional(),
26 | });
27 |
28 | export const usePersonForm = createForm({
29 | initialValues: {
30 | firstName: "",
31 | lastName: "",
32 | age: null,
33 | address: {
34 | street: "",
35 | city: "",
36 | zipCode: "",
37 | },
38 | },
39 | validationSchema,
40 | mode: "onChange",
41 | });
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "createform",
4 | "scripts": {
5 | "build": "turbo run build",
6 | "dev": "turbo run dev --no-cache --continue",
7 | "lint": "turbo run lint",
8 | "clean": "turbo run clean && rm -rf node_modules",
9 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
10 | "prepare": "husky install",
11 | "test": "turbo run test",
12 | "commit": "git-cz"
13 | },
14 | "devDependencies": {
15 | "eslint": "^8.29.0",
16 | "eslint-config-createform": "workspace:*",
17 | "prettier": "^2.8.0",
18 | "turbo": "^2.1.3",
19 | "@turbo-version/version": "0.5.7",
20 | "@turbo-version/release": "0.5.7",
21 | "@commitlint/cli": "^17.0.3",
22 | "@commitlint/config-conventional": "^17.0.3",
23 | "commitizen": "^4.2.4",
24 | "husky": "^8.0.0",
25 | "cz-conventional-changelog": "^3.3.0"
26 | },
27 | "config": {
28 | "commitizen": {
29 | "path": "./node_modules/cz-conventional-changelog"
30 | }
31 | },
32 | "packageManager": "pnpm@9.0.6"
33 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. 👏
2 |
3 | ## Instructions
4 |
5 | These steps will guide you through contributing to this project:
6 |
7 | - Fork the repo
8 | - Clone it and install dependencies
9 |
10 | git clone https://github.com/jucian0/createform
11 | pnpm install
12 |
13 | Keep in mind that after running `pnpm install` the git repo is reset. So a good way to cope with this is to have a copy of the folder to push the changes, and the other to try them.
14 |
15 | Make and commit your changes. Make sure to use `pnpm commit`, and follow all commits steps.
16 |
17 | ## Docs
18 |
19 | To run docs: `pnpm dev` `localhost:3000`
20 |
21 | To run examples: `pnpm dev` `localhost:4200`
22 |
23 | Finally send a [GitHub Pull Request](https://github.com/jucian0/createform/compare?expand=1) with a clear list of what you've done (read more [about pull requests](https://help.github.com/articles/about-pull-requests/)). Make sure all of your commits are atomic (one feature per commit).
24 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md.txt:
--------------------------------------------------------------------------------
1 | THIS PROJECT IS IN MAINTENANCE MODE. We accept pull-requests for Bug Fixes **ONLY**. NO NEW FEATURES ACCEPTED!
2 |
3 |
4 |
5 | ## Description
6 |
7 |
8 | ## Related Issue
9 |
10 |
11 |
12 |
13 |
14 | ## Motivation and Context
15 |
16 |
17 |
18 | ## How Has This Been Tested?
19 |
20 |
21 |
22 |
23 | ## Screenshots (if appropriate):
24 |
--------------------------------------------------------------------------------
/packages/object-utils/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [4.0.7](https://github.com/jucian0/createform/compare/v4.0.6...v4.0.7) (2024-10-07)
2 |
3 |
4 |
5 | ## [4.0.6](https://github.com/jucian0/createform/compare/v4.0.5...v4.0.6) (2024-10-06)
6 |
7 |
8 |
9 | ## [4.0.5](https://github.com/jucian0/createform/compare/v4.0.4...v4.0.5) (2024-07-21)
10 |
11 |
12 |
13 | ## [4.0.4](https://github.com/jucian0/createform/compare/v4.0.3...v4.0.4) (2024-03-10)
14 |
15 |
16 |
17 | ## [4.0.3](https://github.com/jucian0/createform/compare/v4.0.1...v4.0.3) (2024-03-10)
18 |
19 |
20 |
21 | ## [4.0.1](https://github.com/jucian0/createform/compare/v4.0.0...v4.0.1) (2023-06-04)
22 |
23 |
24 |
25 | # [4.0.0](https://github.com/jucian0/createform/compare/v3.5.2...v4.0.0) (2023-06-04)
26 |
27 |
28 |
29 | ## [3.5.2](https://github.com/jucian0/createform/compare/v3.5.1...v3.5.2) (2023-05-29)
30 |
31 |
32 |
33 | ## [3.5.1](https://github.com/jucian0/createform/compare/v3.5.0...v3.5.1) (2023-05-24)
34 |
35 |
36 |
37 | # [3.5.0](https://github.com/jucian0/createform/compare/v3.4.0...v3.5.0) (2023-05-23)
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/validation/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [4.0.7](https://github.com/jucian0/createform/compare/v4.0.6...v4.0.7) (2024-10-07)
2 |
3 |
4 |
5 | ## [4.0.6](https://github.com/jucian0/createform/compare/v4.0.5...v4.0.6) (2024-10-06)
6 |
7 |
8 |
9 | ## [4.0.5](https://github.com/jucian0/createform/compare/v4.0.4...v4.0.5) (2024-07-21)
10 |
11 |
12 |
13 | ## [4.0.4](https://github.com/jucian0/createform/compare/v4.0.3...v4.0.4) (2024-03-10)
14 |
15 |
16 |
17 | ## [4.0.3](https://github.com/jucian0/createform/compare/v4.0.1...v4.0.3) (2024-03-10)
18 |
19 |
20 |
21 | ## [4.0.1](https://github.com/jucian0/createform/compare/v4.0.0...v4.0.1) (2023-06-04)
22 |
23 |
24 |
25 | # [4.0.0](https://github.com/jucian0/createform/compare/v3.5.2...v4.0.0) (2023-06-04)
26 |
27 |
28 |
29 | ## [3.5.2](https://github.com/jucian0/createform/compare/v3.5.1...v3.5.2) (2023-05-29)
30 |
31 |
32 |
33 | ## [3.5.1](https://github.com/jucian0/createform/compare/v3.5.0...v3.5.1) (2023-05-24)
34 |
35 |
36 |
37 | # [3.5.0](https://github.com/jucian0/createform/compare/v3.4.0...v3.5.0) (2023-05-23)
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/react/src/create-form/fields-utils.ts:
--------------------------------------------------------------------------------
1 | import { Field } from "./types";
2 |
3 | /**
4 | * Returns a boolean indicating whether the passed field is a checkbox.
5 | *
6 | * @param {Field} field The field to check.
7 | * @returns {boolean} True if the field is a checkbox, false otherwise.
8 | */
9 | export function isCheckbox(field: any): boolean {
10 | return field.type === "checkbox";
11 | }
12 |
13 | /**
14 | * Returns a boolean indicating whether the passed field is a radio button.
15 | *
16 | * @param {Field} field The field to check.
17 | * @returns {boolean} True if the field is a radio button, false otherwise.
18 | */
19 | export function isRadio(field: Field): boolean {
20 | return !!field?.querySelector('input[type="radio"]');
21 | }
22 |
23 | /**
24 | * Extracts an array of radio elements within a given field.
25 | *
26 | * @param {Field} field The field to extract radio elements from.
27 | * @returns {Field[]} An array of radio elements.
28 | */
29 | export function extractRadioElements(field: Field) {
30 | return Array.from(field.querySelectorAll('input[type="radio"]'));
31 | }
32 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jucian0
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.
--------------------------------------------------------------------------------
/docs/app/routes/components/icon.tsx:
--------------------------------------------------------------------------------
1 | export const CreateFormIcon = () => {
2 | return (
3 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/docs/content/docs/useform/validation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Validation
3 | description: This document explains how to add validation to the form.
4 | tags: react, form, useform, createform, use-form, hook, validation, zod, yup
5 | ---
6 |
7 | Let's add some validation to the form. First we need to create a validation schema. As `createForm`, `useForm` allows to use Zod or Yup validation, in this example we are going to use Zod.
8 |
9 | ```jsx
10 | import { z } from "zod";
11 |
12 | const schema = z.object({
13 | name: z.string().min(3),
14 | });
15 | ```
16 |
17 | After that, we just need to add the validation schema to the form.
18 |
19 | ```jsx
20 | import { useForm } from "@createform/react";
21 |
22 | const schema = z.object({
23 | name: z.string().min(3),
24 | });
25 |
26 | function App() {
27 | const { register } = useForm({ validationSchema: schema });
28 | return (
29 |
33 | );
34 | }
35 | ```
36 |
37 |
38 | You can use Yup, Zod, or your own validation library, or write your own
39 | validation logic.
40 |
41 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "build": "react-router build",
7 | "dev": "react-router dev",
8 | "start": "react-router-serve ./build/server/index.js",
9 | "typecheck": "react-router typegen && tsc"
10 | },
11 | "dependencies": {
12 | "@fumadocs/mdx-remote": "1.3.4",
13 | "@react-router/node": "7.6.3",
14 | "@react-router/serve": "7.6.3",
15 | "framer-motion": "12.23.6",
16 | "fumadocs-core": "15.6.4",
17 | "fumadocs-ui": "15.6.4",
18 | "gray-matter": "4.0.3",
19 | "isbot": "5.1.28",
20 | "react": "19.2.0",
21 | "react-dom": "19.2.0",
22 | "react-router": "7.6.3",
23 | "shiki": "3.8.0",
24 | "@createform/react": "workspace:*",
25 | "zod": "4.1.12",
26 | "react-select": "5.10.2",
27 | "react-datepicker": "8.8.0"
28 | },
29 | "devDependencies": {
30 | "@react-router/dev": "7.6.3",
31 | "@tailwindcss/vite": "4.1.11",
32 | "@types/node": "24.0.13",
33 | "@types/react": "19.2.2",
34 | "@types/react-dom": "19.2.2",
35 | "react-router-devtools": "5.0.6",
36 | "tailwindcss": "4.1.11",
37 | "typescript": "5.8.3",
38 | "vite": "7.0.4",
39 | "vite-tsconfig-paths": "5.1.4"
40 | },
41 | "version": "3.0.2"
42 | }
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStepWithYup/basic-info-step.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Input, Text } from "@chakra-ui/react";
2 | import { usePersonForm } from "./use-person-form";
3 | import { Button, Stack } from "@chakra-ui/react";
4 | import { useWizard } from "react-use-wizard";
5 | import * as yup from "yup";
6 | import React from "react";
7 |
8 | export function BasicInfoStep() {
9 | const { previousStep, nextStep } = useWizard();
10 | const { register, state } = usePersonForm();
11 | const { touched, errors } = state;
12 |
13 | return (
14 |
15 | Basic Info
16 |
17 | {touched.firstName && errors.firstName}
18 |
19 | {touched.lastName && errors.lastName}
20 |
21 | {touched.age && errors.age}
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@createform/examples",
3 | "version": "4.0.7",
4 | "description": "Examples of Createform",
5 | "author": "Jucian0 ",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/jucian0/createform",
9 | "homepage": "https://useform.org/"
10 | },
11 | "scripts": {
12 | "dev": "vite"
13 | },
14 | "license": "MIT",
15 | "dependencies": {
16 | "@emotion/react": "11.10.6",
17 | "@emotion/styled": "11.10.6",
18 | "react-datepicker": "4.8.0",
19 | "react-select": "^5.6.1",
20 | "react-test-renderer": "18.2.0",
21 | "react-use-wizard": "2.2.1",
22 | "vite": "4.5.5",
23 | "vite-plugin-eslint": "^1.8.1",
24 | "vite-tsconfig-paths": "4.0.5",
25 | "vitest": "0.25.8",
26 | "yup": "^0.27.0",
27 | "zod": "^3.22.3",
28 | "@chakra-ui/react": "2.5.5",
29 | "framer-motion": "^10.11.5",
30 | "@createform/react": "workspace:*",
31 | "react": "18.2.0",
32 | "react-dom": "18.2.0"
33 | },
34 | "devDependencies": {
35 | "@types/react-datepicker": "^4.8.0",
36 | "@types/yup": "^0.29.13",
37 | "@vitejs/plugin-react": "3.0.1",
38 | "@types/react": "^18.0.31",
39 | "@types/react-dom": "18.0.11",
40 | "@createform/tsconfig": "workspace:*",
41 | "eslint": "^8.37.0"
42 | }
43 | }
--------------------------------------------------------------------------------
/packages/react/src/create-form/debounce.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns a debounced version of the passed function that will only be executed after `wait` milliseconds
3 | * have elapsed since the last time the debounced function was invoked.
4 | *
5 | *
6 | * @template TThis The type of `this` to be used for the function.
7 | * @template TFn The type of function to debounce.
8 | * @param this The `this` context for the function.
9 | * @param fn The function to debounce.
10 | * @param wait The number of milliseconds to wait before executing the debounced function.
11 | * @param [immediate=false] A boolean indicating whether the function should be executed immediately
12 | * before the debounce timer starts.
13 | * @returns A debounced version of the passed function.
14 | */
15 | export function debounce(
16 | this: TThis,
17 | fn: TFn,
18 | wait: number,
19 | immediate?: boolean
20 | ) {
21 | let timeout: any;
22 |
23 | return (...args: Array) => {
24 | const context = this;
25 |
26 | const later = () => {
27 | timeout = null;
28 | if (!immediate) fn.apply(context, args);
29 | };
30 |
31 | const callNow = immediate && !timeout;
32 | clearTimeout(timeout);
33 | timeout = setTimeout(later, wait);
34 |
35 | if (callNow) {
36 | fn.apply(context, args);
37 | }
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/docs/app/source.ts:
--------------------------------------------------------------------------------
1 | import {
2 | loader,
3 | type MetaData,
4 | type PageData,
5 | type Source,
6 | type VirtualFile,
7 | } from "fumadocs-core/source";
8 | import matter from "gray-matter";
9 | import * as path from "node:path";
10 |
11 | const files = Object.entries(
12 | import.meta.glob("/content/docs/**/*", {
13 | eager: true,
14 | query: "?raw",
15 | import: "default",
16 | })
17 | );
18 |
19 | const virtualFiles: VirtualFile[] = files.flatMap(([file, content]) => {
20 | const ext = path.extname(file);
21 | const virtualPath = path.relative(
22 | "content/docs",
23 | path.join(process.cwd(), file)
24 | );
25 |
26 | if (ext === ".mdx" || ext === ".md") {
27 | const parsed = matter(content);
28 |
29 | return {
30 | type: "page",
31 | path: virtualPath,
32 | data: {
33 | ...parsed.data,
34 | content: parsed.content,
35 | },
36 | };
37 | }
38 |
39 | if (ext === ".json") {
40 | return {
41 | type: "meta",
42 | path: virtualPath,
43 | data: JSON.parse(content),
44 | };
45 | }
46 |
47 | return [];
48 | });
49 |
50 | export const source = loader({
51 | source: {
52 | files: virtualFiles,
53 | } as Source<{
54 | pageData: PageData & {
55 | content: string;
56 | };
57 | metaData: MetaData;
58 | }>,
59 | baseUrl: "/docs",
60 | });
61 |
--------------------------------------------------------------------------------
/packages/object-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@createform/object-utils",
3 | "version": "4.0.7",
4 | "description": "Immutable object utilities.",
5 | "keywords": [
6 | "react",
7 | "hooks",
8 | "form",
9 | "object",
10 | "immutable",
11 | "form-validation",
12 | "validation",
13 | "typescript",
14 | "react-hooks"
15 | ],
16 | "author": "Jucian0 ",
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/jucian0/createform",
20 | "homepage": "https://useform.org/"
21 | },
22 | "devDependencies": {
23 | "tsup": "6.7.0",
24 | "@createform/tsconfig": "workspace:*",
25 | "jest": "29.5.0",
26 | "ts-jest": "29.0.5",
27 | "@jest/globals": "29.5.0",
28 | "typescript": "4.9.3",
29 | "@types/jest": "29.5.0"
30 | },
31 | "main": "./dist/index.js",
32 | "module": "./dist/index.mjs",
33 | "types": "./dist/index.d.ts",
34 | "sideEffects": false,
35 | "license": "MIT",
36 | "files": [
37 | "dist/**"
38 | ],
39 | "scripts": {
40 | "build": "tsup src/index.ts --format esm,cjs --dts --external react",
41 | "dev": "tsup src/index.ts --format esm,cjs --watch --dts --external react",
42 | "lint": "eslint \"src/**/*.ts*\"",
43 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
44 | "test": "jest",
45 | "test:watch": "jest --watch"
46 | },
47 | "publishConfig": {
48 | "access": "public"
49 | }
50 | }
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStepWithYup/address-step.tsx:
--------------------------------------------------------------------------------
1 | import { Input } from "@chakra-ui/react";
2 | import { Button, Stack, Text, Box } from "@chakra-ui/react";
3 | import { usePersonForm } from "./use-person-form";
4 | import { useWizard } from "react-use-wizard";
5 | import * as yup from "yup";
6 | import React from "react";
7 |
8 | export function AddressStep() {
9 | const { previousStep } = useWizard();
10 | const { register, state } = usePersonForm();
11 | const { touched, errors } = state;
12 |
13 | return (
14 |
15 | Address
16 |
17 |
18 | {touched.address?.street && errors.address?.street}
19 |
20 |
21 |
22 | {touched.address?.city && errors.address?.city}
23 |
24 |
25 |
26 | {touched.address?.zipCode && errors.address?.zipCode}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/docs/content/docs/useform/creating-a-form.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Creating a form
3 | description: This document explains how to create a form with useForm.
4 | tags: react, form, useform, createform, use-form, hook
5 | ---
6 |
7 | Different from `createForm`, `useForm` do not use `register` function to register every input, but only the `` element. The only property required to register a inoput is the `name` property.
8 |
9 | ```jsx
10 | import { useForm } from "@createform/react";
11 |
12 | function App() {
13 | const { register } = useForm();
14 | return (
15 |
19 | );
20 | }
21 | ```
22 |
23 | In the above example, we created a form with two inputs, `name` and `submit`. The submit input triggers the `handleSubmit` function.
24 |
25 | Let's add some functions to handle submit, and reset the form.
26 |
27 | ```jsx
28 | import { useForm } from "@createform/react";
29 |
30 | function App() {
31 | const { register } = useForm({
32 | onSubmit: handleSubmit
33 | onReset: handleReset
34 | });
35 |
36 | function handleSubmit(data) {
37 | console.log(data);
38 | }
39 |
40 | function handlereset() {
41 | console.log(data);
42 | }
43 |
44 | return (
45 |
50 | );
51 | }
52 | ```
53 |
--------------------------------------------------------------------------------
/docs/app/routes/components/pulse.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from "framer-motion";
2 |
3 | const style: any = {
4 | width: "240px",
5 | height: "240px",
6 | borderRadius: "50%",
7 | position: "absolute",
8 | };
9 |
10 | export default function HeroPulse() {
11 | return (
12 |
25 |
38 |
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/yup-validation.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import { Button, Input, Stack, Text } from "@chakra-ui/react";
3 | import * as yup from "yup";
4 | import React from "react";
5 |
6 | const validationSchema = yup.object({
7 | email: yup.string().email().required(),
8 | password: yup.string().min(8).required(),
9 | });
10 |
11 | const useLoginForm = createForm({
12 | initialValues: {
13 | email: "",
14 | password: "",
15 | },
16 | mode: "onSubmit",
17 | validationSchema,
18 | });
19 |
20 | export function FormYupValidation() {
21 | const { register, handleReset, handleSubmit, state } = useLoginForm();
22 | const { errors, touched } = state;
23 |
24 | function onSubmit(e: any) {
25 | console.log(e);
26 | }
27 |
28 | function onReset(e: any) {
29 | console.log(e);
30 | }
31 |
32 | return (
33 |
34 | Form
35 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout Repo
16 | uses: actions/checkout@v3
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Setup pnpm 7
21 | uses: pnpm/action-setup@v2
22 | with:
23 | version: 7
24 |
25 | - name: Setup Node.js 16.x
26 | uses: actions/setup-node@v2
27 | with:
28 | node-version: 16.x
29 |
30 | - name: Install Dependencies
31 | shell: bash
32 | run: pnpm i
33 |
34 | - name: Build
35 | shell: bash
36 | run: pnpm --filter=react... build
37 |
38 | ## We are going to replace it by a specif command to configure git environments
39 | - name: Temporally git user
40 | shell: bash
41 | run: git config --global user.name 'github-actions[bot]' && git config --global user.email 'github-actions[bot]@users.noreply.github.com'
42 |
43 | - name: Turbo Version
44 | shell: bash
45 | run: pnpm turbo-version
46 |
47 | - name: Turbo Release
48 | shell: bash
49 | env:
50 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
52 | run: pnpm turbo-release -s @createform/docs,@createform/examples
53 |
54 | - name: Push tags, and commits
55 | shell: bash
56 | run: git push --tags && git push
57 |
--------------------------------------------------------------------------------
/examples/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples",
3 | "$schema": "../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/examples/src",
5 | "projectType": "application",
6 | "targets": {
7 | "build": {
8 | "executor": "@nrwl/vite:build",
9 | "outputs": [
10 | "{options.outputPath}"
11 | ],
12 | "defaultConfiguration": "production",
13 | "options": {
14 | "outputPath": "dist/packages/examples"
15 | },
16 | "configurations": {
17 | "development": {},
18 | "production": {}
19 | }
20 | },
21 | "serve": {
22 | "executor": "@nrwl/vite:dev-server",
23 | "defaultConfiguration": "development",
24 | "options": {
25 | "buildTarget": "examples:build"
26 | },
27 | "configurations": {
28 | "development": {
29 | "buildTarget": "examples:build:development",
30 | "hmr": true
31 | },
32 | "production": {
33 | "buildTarget": "examples:build:production",
34 | "hmr": false
35 | }
36 | }
37 | },
38 | "test": {
39 | "executor": "@nrwl/vite:test",
40 | "outputs": [
41 | "{projectRoot}/coverage"
42 | ],
43 | "options": {
44 | "passWithNoTests": true
45 | }
46 | },
47 | "lint": {
48 | "executor": "@nrwl/linter:eslint",
49 | "outputs": [
50 | "{options.outputFile}"
51 | ],
52 | "options": {
53 | "lintFilePatterns": [
54 | "packages/examples/**/*.{ts,tsx,js,jsx}"
55 | ]
56 | }
57 | }
58 | },
59 | "tags": []
60 | }
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/zod-validation.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import { Button, Input, Stack, Text } from "@chakra-ui/react";
3 | import { z } from "zod";
4 | import React from "react";
5 |
6 | const validationSchema = z.object({
7 | email: z.string().email(),
8 | password: z.string().min(8),
9 | });
10 |
11 | const useLoginForm = createForm({
12 | initialValues: {
13 | email: "",
14 | password: "",
15 | },
16 | mode: "onSubmit",
17 | validationSchema,
18 | });
19 |
20 | export function FormZodValidation() {
21 | const { register, handleReset, handleSubmit, state } = useLoginForm();
22 | const { errors, touched } = state;
23 |
24 | function onSubmit(e: any) {
25 | window.location.search = `?email=${e.email}&password=${e.password}`;
26 | }
27 |
28 | function onReset(e: any) {
29 | window.location.search = "";
30 | }
31 |
32 | return (
33 |
34 | Form
35 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/packages/validation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@createform/validation",
3 | "version": "4.0.7",
4 | "description": "The ReactJS form library.",
5 | "keywords": [
6 | "react",
7 | "hooks",
8 | "form",
9 | "forms",
10 | "form-validation",
11 | "validation",
12 | "typescript",
13 | "react-hooks"
14 | ],
15 | "author": "Jucian0 ",
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/jucian0/useform",
19 | "homepage": "https://useform.org/"
20 | },
21 | "dependencies": {
22 | "@createform/object-utils": "workspace:*"
23 | },
24 | "devDependencies": {
25 | "tsup": "6.7.0",
26 | "@createform/tsconfig": "workspace:*",
27 | "jest": "29.5.0",
28 | "ts-jest": "29.0.5",
29 | "jest-each": "29.5.0",
30 | "@jest/globals": "29.5.0",
31 | "yup": "1.0.2",
32 | "@types/yup": "0.29.13",
33 | "zod": "3.22.3",
34 | "@faker-js/faker": "7.4.0",
35 | "typescript": "4.9.3",
36 | "@types/jest": "29.5.0"
37 | },
38 | "main": "./dist/index.js",
39 | "module": "./dist/index.mjs",
40 | "types": "./dist/index.d.ts",
41 | "sideEffects": false,
42 | "license": "MIT",
43 | "files": [
44 | "dist/**"
45 | ],
46 | "scripts": {
47 | "build": "tsup src/index.ts --format esm,cjs --dts --external react",
48 | "dev": "tsup src/index.ts --format esm,cjs --watch --dts --external react",
49 | "lint": "eslint \"src/**/*.ts*\"",
50 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
51 | "test": "jest",
52 | "test:watch": "jest --watch"
53 | },
54 | "publishConfig": {
55 | "access": "public"
56 | }
57 | }
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/radio-and-checkbox.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import { Button, Stack } from "@chakra-ui/react";
3 | import React from "react";
4 |
5 | const useLoginForm = createForm({
6 | initialValues: {
7 | agree: true,
8 | gender: "masc",
9 | },
10 | mode: "onSubmit",
11 | });
12 |
13 | export function RadioAndCheckboxExample() {
14 | const { state, register, handleReset, handleSubmit, setFieldValue } =
15 | useLoginForm();
16 |
17 | console.log("state", state.values, "changed");
18 |
19 | function onSubmit(e: any) {
20 | console.log(e);
21 | }
22 |
23 | function onReset(e: any) {
24 | console.log(e);
25 | }
26 |
27 | return (
28 |
29 | Form
30 |
53 |
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/docs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.0.2](https://github.com/jucian0/turboversion/compare/v3.0.1...v3.0.2) (2025-07-30)
2 |
3 |
4 |
5 | ## [3.0.1](https://github.com/jucian0/turboversion/compare/v3.0.0...v3.0.1) (2025-07-27)
6 |
7 |
8 |
9 | # [2.0.0](https://github.com/jucian0/turboversion/compare/v1.0.0...v2.0.0) (2025-07-23)
10 |
11 |
12 |
13 | # [1.0.0](https://github.com/jucian0/turboversion/compare/v0.10.0...v1.0.0) (2025-07-20)
14 |
15 |
16 | ### Bug Fixes
17 |
18 | * **workspace:** rename synced to sync ([afbfc64](https://github.com/jucian0/turboversion/commit/afbfc645c10474c85541365182e0baacdacf282d))
19 |
20 |
21 | ### BREAKING CHANGES
22 |
23 | * **workspace:** Rename synced to sync
24 |
25 |
26 |
27 | # [0.10.0](https://github.com/jucian0/turboversion/compare/v0.9.0...v0.10.0) (2025-07-20)
28 |
29 |
30 |
31 | # [0.9.0](https://github.com/jucian0/turboversion/compare/v0.8.7...v0.9.0) (2025-07-20)
32 |
33 |
34 | ### Features
35 |
36 | * **workspace:** rename libraries ([9ee9f10](https://github.com/jucian0/turboversion/commit/9ee9f10ff399984310b8a9ab8947f4cdc8339729))
37 |
38 |
39 |
40 | ## [0.8.7](https://github.com/jucian0/turboversion/compare/v0.8.6...v0.8.7) (2025-07-18)
41 |
42 |
43 |
44 | ## [0.8.6](https://github.com/jucian0/turboversion/compare/v0.8.5...v0.8.6) (2025-07-18)
45 |
46 |
47 |
48 | ## [0.8.5](https://github.com/jucian0/turboversion/compare/v0.8.4...v0.8.5) (2025-07-18)
49 |
50 |
51 |
52 | ## [0.8.4](https://github.com/jucian0/turboversion/compare/v0.8.3...v0.8.4) (2025-07-15)
53 |
54 |
55 |
56 | ## [0.8.3](https://github.com/jucian0/turboversion/compare/v0.8.2...v0.8.3) (2025-02-17)
57 |
58 |
59 |
60 | ## [0.8.2](https://github.com/jucian0/turboversion/compare/v0.8.1...v0.8.2) (2025-02-17)
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/yup-inline-validation.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import { Button, Input, Stack, Text } from "@chakra-ui/react";
3 | import * as yup from "yup";
4 | import React from "react";
5 |
6 | const useLoginForm = createForm({
7 | initialValues: {
8 | email: "juciano",
9 | password: "12d",
10 | },
11 | mode: "onChange",
12 | });
13 |
14 | export function FormZodInlineValidation() {
15 | const { register, handleReset, handleSubmit, state } = useLoginForm();
16 | const { errors, touched } = state;
17 |
18 | function onSubmit(e: any) {
19 | console.log(e);
20 | }
21 |
22 | function onReset(e: any) {
23 | console.log(e);
24 | }
25 |
26 | return (
27 |
28 | Form
29 |
57 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/docs/content/docs/createform/form-submission.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Form submission
3 | description: This doc helps you how to submit a form using createForm.
4 | tags: react, form, useform, createform, hook, loadData
5 | ---
6 |
7 | ## Introduction
8 |
9 | Once a user fills out a form, it needs to be submitted to the server. There are multiple ways to achieve this. Let's explore how to make it work.
10 |
11 | ## Submit form when deffining the form
12 |
13 | You can configure the form submission when defining the form by using the onSubmit property of createForm. This property will be called when the form is submitted.
14 |
15 | ```jsx
16 | import { createForm } from "@createform/react";
17 |
18 | const form = createForm({
19 | onSubmit: async (values) => {
20 | console.log(values);
21 | }
22 | })
23 | ```
24 |
25 | ## Submit form when using the hook form
26 |
27 | Typically, when updating data, the application should redirect to another page. However, in specific cases, you may want to reload the data to display the new information using the reloadData function.
28 |
29 | ```jsx
30 | import { createForm } from "@createform/react";
31 |
32 | const form = createForm({
33 | initialValues: {
34 | name: "",
35 | email: "",
36 | phone: "",
37 | },
38 |
39 | })
40 |
41 | const FormExample = () => {
42 | cont {handleSubmit} = form();
43 |
44 | return (
45 |
50 | )
51 | }
52 | ```
53 |
54 | ## Conclusion
55 |
56 | You now have a better understanding of how to submit a form using createForm. The choice of how to submit the form is up to you.
--------------------------------------------------------------------------------
/docs/content/docs/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: About
3 | description: CreateForm is an open-source JavaScript library for building user-friendly and responsive forms.
4 | tags: react, form, useform, createform, use-form, hook
5 | ---
6 |
7 | CreateForm is an open-source JavaScript library for building user-friendly and responsive forms. With a simple and intuitive API, CreateForm makes it easy to create complex forms with dynamic elements and validation in just a few lines of code.
8 |
9 | ## Features:
10 |
11 | - Easy integration: CreateForm can be easily integrated with any modern JavaScript framework or no-framework project.
12 | - Dynamic form elements: CreateForm makes it easy to add, remove, or update form elements dynamically.
13 | - Validation: CreateForm supports a wide range of validation rules, including custom validation functions, to ensure that the form data is accurate and reliable.
14 | - Responsive design: CreateForm forms are fully responsive, adapting to the screen size and orientation of the user's device.
15 | - Cross-browser compatibility: CreateForm is compatible with all modern browsers, including Chrome, Firefox, Safari, and Edge.
16 |
17 | ## Getting Started:
18 |
19 | To get started with CreateForm, simply install the library using npm or yarn, and import it into your project. Then, use the CreateForm API to build your form, adding form elements, validation rules, and custom styles as needed.
20 |
21 | ## Documentation:
22 |
23 | Full documentation and usage examples are available on the CreateForm website, along with a community forum for support and discussion.
24 |
25 | ## Conclusion:
26 |
27 | Whether you're building a simple contact form or a complex data entry application, CreateForm has the features and flexibility you need to get the job done. So why wait? Start building your forms with CreateForm today!
28 |
--------------------------------------------------------------------------------
/docs/app/app.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 | @import "fumadocs-ui/css/preset.css";
3 |
4 |
5 | @theme {
6 | --color-fd-background: hsl(0, 0%, 96%);
7 | --color-fd-foreground: hsl(0, 0%, 3.9%);
8 | --color-fd-muted: hsl(0, 0%, 96.1%);
9 | --color-fd-muted-foreground: hsl(0, 0%, 45.1%);
10 | --color-fd-popover: hsl(0, 0%, 98%);
11 | --color-fd-popover-foreground: hsl(0, 0%, 15.1%);
12 | --color-fd-card: hsl(0, 0%, 94.7%);
13 | --color-fd-card-foreground: hsl(0, 0%, 3.9%);
14 | --color-fd-border: hsla(0, 0%, 62%, 20%);
15 | --color-fd-primary: #2EC4B6;
16 | --color-fd-primary-foreground: hsl(0, 0%, 98%);
17 | --color-fd-secondary: hsl(0, 0%, 93.1%);
18 | --color-fd-secondary-foreground: hsl(0, 0%, 9%);
19 | --color-fd-accent: hsla(0, 0%, 82%, 50%);
20 | --color-fd-accent-foreground: hsl(0, 0%, 9%);
21 | --color-fd-ring: hsl(0, 0%, 63.9%);
22 | }
23 |
24 | .dark {
25 | --color-fd-background: hsl(0, 0%, 7.04%);
26 | --color-fd-foreground: hsl(0, 0%, 92%);
27 | --color-fd-muted: hsl(0, 0%, 12.9%);
28 | --color-fd-muted-foreground: hsla(0, 0%, 70%, 0.8);
29 | --color-fd-popover: hsl(0, 0%, 10.6%);
30 | --color-fd-popover-foreground: hsl(0, 0%, 86.9%);
31 | --color-fd-card: hsl(0, 0%, 9.8%);
32 | --color-fd-card-foreground: hsl(0, 0%, 98%);
33 | --color-fd-border: hsla(0, 0%, 40%, 20%);
34 | --color-fd-primary: #2EC4B6;
35 | --color-fd-primary-foreground: hsl(0, 0%, 9%);
36 | --color-fd-secondary: hsl(0, 0%, 12.9%);
37 | --color-fd-secondary-foreground: hsl(0, 0%, 92%);
38 | --color-fd-accent: hsla(0, 0%, 40.9%, 30%);
39 | --color-fd-accent-foreground: hsl(0, 0%, 90%);
40 | --color-fd-ring: hsl(0, 0%, 54.9%);
41 | }
42 |
43 | .dark #nd-sidebar {
44 | --color-fd-muted: hsl(0, 0%, 16%);
45 | --color-fd-secondary: hsl(0, 0%, 18%);
46 | --color-fd-muted-foreground: hsl(0, 0%, 72%);
47 | }
48 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/MultiStep/basic-info-step.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Input, Text } from "@chakra-ui/react";
2 | import { usePersonForm } from "./use-person-form";
3 | import { Button, Stack } from "@chakra-ui/react";
4 | import { useWizard } from "react-use-wizard";
5 | import Select from "react-select";
6 | import ReactDatePicker from "react-datepicker";
7 | import React from "react";
8 |
9 | export function BasicInfoStep() {
10 | const { nextStep } = useWizard();
11 | const { register, state, setFieldValue, setFieldTouched } = usePersonForm();
12 | const { touched, errors, values } = state;
13 |
14 | return (
15 |
16 | Basic Info
17 | setFieldTouched("date")}
21 | onChange={(e) => setFieldValue("date", e)}
22 | value={values.date.toString()}
23 | />
24 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/packages/validation/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as Dot from "@createform/object-utils";
2 |
3 | export function makeDotNotation(str: string) {
4 | return str.split("[").join(".").split("]").join("");
5 | }
6 |
7 | export async function validate(
8 | values: TValues,
9 | validationSchema: any
10 | ) {
11 | if (validationSchema?._def?.typeName) {
12 | return validationSchema
13 | .parseAsync(values)
14 | .then(() => {
15 | return {};
16 | })
17 | .catch((e: any) => {
18 | throw JSON.parse(e).reduce((acc: {}, key: any) => {
19 | const path = key.path.length > 0 ? key.path.join(".") : "message";
20 | return Dot.set(acc, path, key.message);
21 | }, {});
22 | });
23 | }
24 |
25 | return validationSchema
26 | ?.validate(values, { abortEarly: false })
27 | .then(() => {
28 | return {};
29 | })
30 | .catch((e: any) => {
31 | throw e.inner.reduce((acc: {}, key: any) => {
32 | const path = makeDotNotation(key.path || "message");
33 | return Dot.set(acc, path, key.message);
34 | }, {});
35 | });
36 | }
37 |
38 | export function validateSync(
39 | values: TValues,
40 | validationSchema: any
41 | ) {
42 | if (validationSchema?._def?.typeName) {
43 | try {
44 | validationSchema.parse(values);
45 | return {};
46 | } catch (e) {
47 | return JSON.parse(e as any).reduce((acc: {}, key: any) => {
48 | const path = key.path.length > 0 ? key.path.join(".") : "message";
49 | return Dot.set(acc, path, key.message);
50 | }, {});
51 | }
52 | }
53 | try {
54 | validationSchema?.validateSync(values, { abortEarly: false });
55 | return {};
56 | } catch (e) {
57 | return (e as any).inner.reduce((acc: {}, key: any) => {
58 | const path = makeDotNotation(key.path || "message");
59 | return Dot.set(acc, path, key.message);
60 | }, {});
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@createform/react",
3 | "version": "4.0.7",
4 | "description": "The ReactJS form library.",
5 | "keywords": [
6 | "react",
7 | "hooks",
8 | "form",
9 | "forms",
10 | "form-validation",
11 | "validation",
12 | "typescript",
13 | "react-hooks"
14 | ],
15 | "author": "Jucian0 ",
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/jucian0/useform",
19 | "homepage": "https://useform.org/"
20 | },
21 | "dependencies": {
22 | "@createform/validation": "workspace:*",
23 | "@createform/object-utils": "workspace:*"
24 | },
25 | "devDependencies": {
26 | "tsup": "6.7.0",
27 | "@createform/tsconfig": "workspace:*",
28 | "jest": "29.5.0",
29 | "ts-jest": "29.0.5",
30 | "jest-each": "29.5.0",
31 | "@jest/globals": "29.5.0",
32 | "yup": "1.0.2",
33 | "@types/yup": "0.29.13",
34 | "@types/react": "18.0.31",
35 | "zod": "3.22.3",
36 | "@faker-js/faker": "7.4.0",
37 | "typescript": "4.9.3",
38 | "@testing-library/react": "13.4.0",
39 | "@types/jest": "29.5.0",
40 | "@testing-library/jest-dom": "5.16.5",
41 | "jest-environment-jsdom": "29.5.0",
42 | "react-dom": "19.2.0",
43 | "react": "19.2.0"
44 | },
45 | "peerDependencies": {
46 | "react": ">=18.0.0"
47 | },
48 | "main": "./dist/index.js",
49 | "module": "./dist/index.mjs",
50 | "types": "./dist/index.d.ts",
51 | "sideEffects": false,
52 | "license": "MIT",
53 | "files": [
54 | "dist/**"
55 | ],
56 | "scripts": {
57 | "build": "tsup src/index.ts --format esm,cjs --dts --external react",
58 | "dev": "tsup src/index.ts --format esm,cjs --watch --dts --external react",
59 | "lint": "eslint \"src/**/*.ts*\"",
60 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
61 | "test": "jest",
62 | "test:watch": "jest --watch"
63 | },
64 | "publishConfig": {
65 | "access": "public"
66 | }
67 | }
--------------------------------------------------------------------------------
/docs/app/docs/page.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from "./+types/page";
2 | import { DocsLayout } from "fumadocs-ui/layouts/docs";
3 | import {
4 | DocsBody,
5 | DocsDescription,
6 | DocsPage,
7 | DocsTitle,
8 | } from "fumadocs-ui/page";
9 | import { source } from "@/source";
10 | import { getMDXComponents } from "./mdx-components";
11 | import { executeMdxSync } from "@fumadocs/mdx-remote/client";
12 | import type { PageTree } from "fumadocs-core/server";
13 | import { createCompiler } from "@fumadocs/mdx-remote";
14 | import * as path from "node:path";
15 |
16 | const compiler = createCompiler({
17 | development: false,
18 | });
19 |
20 | export async function loader({ params }: Route.LoaderArgs) {
21 | const slugs = params["*"].split("/").filter((v) => v.length > 0);
22 | const page = source.getPage(slugs);
23 | if (!page) throw new Response("Not found", { status: 404 });
24 |
25 | const compiled = await compiler.compileFile({
26 | path: path.resolve("content/docs", page.path),
27 | value: page.data.content,
28 | });
29 |
30 | return {
31 | page,
32 | compiled: compiled.toString(),
33 | tree: source.pageTree,
34 | };
35 | }
36 |
37 | export default function Page(props: Route.ComponentProps) {
38 | const { page, compiled, tree } = props.loaderData;
39 | const { default: Mdx, toc } = executeMdxSync(compiled);
40 |
41 | return (
42 | ,
45 |
46 | }}
47 | tree={tree as PageTree.Root}
48 | >
49 | {"Createform - " + page.data.title}
50 |
51 |
52 | {"Createform - " + page.data.title}
53 | {page.data.description}
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/packages/object-utils/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import * as Dot from ".";
2 |
3 | describe("Dot set", () => {
4 | it("Should set a value", () => {
5 | const obj = (value: string) => ({ foo: value });
6 | const newValue = "baz";
7 | const newObj = Dot.set(obj("bar"), "foo", newValue);
8 | expect(newObj).toEqual(obj(newValue));
9 | });
10 |
11 | it("Should set a value in an array", () => {
12 | const obj = (value: string) => ({ foo: [value] });
13 | const newValue = "bar";
14 | const newObj = Dot.set(obj("bar"), "foo[0]", newValue);
15 | expect(newObj).toEqual(obj(newValue));
16 | });
17 |
18 | it("Should set a value in an array with a number", () => {
19 | const newValue = "baz";
20 | const newObj = Dot.set({ foo: [] }, "foo.1", newValue);
21 | expect(newObj).toEqual({ foo: [undefined, newValue] });
22 | });
23 |
24 | it("Should set a value in a nested object", () => {
25 | const newValue = "baz";
26 | const newObj = Dot.set({ foo: { bar: "bar" } }, "foo.bar", newValue);
27 | expect(newObj).toEqual({ foo: { bar: newValue } });
28 | });
29 | });
30 |
31 | describe("Dot get", () => {
32 | it("Should get a value", () => {
33 | const obj = { foo: "bar" };
34 | expect(Dot.get(obj, "foo")).toEqual("bar");
35 | });
36 |
37 | it("Should get a value in an array", () => {
38 | const obj = { foo: ["bar"] };
39 | expect(Dot.get(obj, "foo[0]")).toEqual("bar");
40 | });
41 |
42 | it("Should get a value in an array with a number", () => {
43 | const obj = { foo: [undefined, "bar"] };
44 | expect(Dot.get(obj, "foo.1")).toEqual("bar");
45 | });
46 |
47 | it("Should get a value in a nested object", () => {
48 | const obj = { foo: { bar: "bar" } };
49 | expect(Dot.get(obj, "foo.bar")).toEqual("bar");
50 | });
51 |
52 | it("Should return undefined when property does not exist", () => {
53 | const obj = { foo: "bar" };
54 | expect(Dot.get(obj, "bar")).toEqual(undefined);
55 | });
56 |
57 | it("Should return undefined when property does not exist in an array", () => {
58 | const obj = { foo: ["bar"] };
59 | expect(Dot.get(obj, "foo[1]")).toEqual(undefined);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/zod-inline-validation.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 | import { Button, Input, Stack, Text } from "@chakra-ui/react";
3 | import { z } from "zod";
4 | import React from "react";
5 |
6 | const useLoginForm = createForm({
7 | initialValues: {
8 | // email: 'juciano',
9 | // password: '12d',
10 | },
11 | mode: "onChange",
12 | });
13 |
14 | export function FormZodInlineValidation() {
15 | const { register, handleReset, handleSubmit, state } = useLoginForm();
16 | const { errors, touched } = state;
17 |
18 | function onSubmit(e: any) {
19 | console.log(e);
20 | }
21 |
22 | function onReset(e: any) {
23 | console.log(e);
24 | }
25 |
26 | return (
27 |
28 | Form
29 |
85 |
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/docs/app/root.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | isRouteErrorResponse,
3 | Links,
4 | Meta,
5 | Outlet,
6 | Scripts,
7 | ScrollRestoration,
8 | } from 'react-router';
9 | import { RootProvider } from 'fumadocs-ui/provider/base';
10 | import { ReactRouterProvider } from 'fumadocs-core/framework/react-router';
11 | import type { Route } from './+types/root';
12 | import './app.css';
13 |
14 | export const links: Route.LinksFunction = () => [
15 | { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
16 | {
17 | rel: 'preconnect',
18 | href: 'https://fonts.gstatic.com',
19 | crossOrigin: 'anonymous',
20 | },
21 | {
22 | rel: 'stylesheet',
23 | href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
24 | },
25 | ];
26 |
27 | export function Layout({ children }: { children: React.ReactNode }) {
28 | return (
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {children}
39 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default function App() {
48 | return ;
49 | }
50 |
51 | export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
52 | let message = 'Oops!';
53 | let details = 'An unexpected error occurred.';
54 | let stack: string | undefined;
55 |
56 | if (isRouteErrorResponse(error)) {
57 | message = error.status === 404 ? '404' : 'Error';
58 | details =
59 | error.status === 404
60 | ? 'The requested page could not be found.'
61 | : error.statusText || details;
62 | } else if (import.meta.env.DEV && error && error instanceof Error) {
63 | details = error.message;
64 | stack = error.stack;
65 | }
66 |
67 | return (
68 |
69 | {message}
70 | {details}
71 | {stack && (
72 |
73 | {stack}
74 |
75 | )}
76 |
77 | );
78 | }
79 |
--------------------------------------------------------------------------------
/docs/app/demos/native-demo.tsx:
--------------------------------------------------------------------------------
1 | import { createForm } from "@createform/react";
2 |
3 | const useForm = createForm({
4 | initialValues: {
5 | email: "",
6 | },
7 | mode: "onChange",
8 | });
9 |
10 | export function NativeFormDemo() {
11 | const { register, handleSubmit, state, handleReset } = useForm();
12 | const { errors, touched } = state;
13 |
14 | return (
15 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/examples/src/Examples/createForm/form-example.tsx:
--------------------------------------------------------------------------------
1 | import { CreateForm, createForm } from "@createform/react";
2 | import { Button, Input, Select, Stack } from "@chakra-ui/react";
3 | import React from "react";
4 |
5 | type Form = CreateForm<
6 | {
7 | email: string;
8 | password: string;
9 | options: string;
10 | },
11 | string
12 | >;
13 |
14 | const useLoginForm = createForm