Skip to content

Commit 31f6261

Browse files
feat: valibot form adapter (#499)
* feat: mount method on FormApi * fix solid-form test case * fix: added form.mount() to tests * feat: valibot-form-adapter * chore: add missing config items * docs: add Valibot React example * docs: add Solid Valibot example * docs: add valibot Vue example * fix: valibot async adapter now works * docs: add docs for valibot adapter --------- Co-authored-by: Corbin Crutchley <[email protected]>
1 parent 1a37fab commit 31f6261

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2150
-1495
lines changed

docs/config.json

+12
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@
101101
{
102102
"label": "Zod",
103103
"to": "framework/react/examples/zod"
104+
},
105+
{
106+
"label": "Valibot",
107+
"to": "framework/react/examples/valibot"
104108
}
105109
]
106110
}
@@ -153,6 +157,10 @@
153157
{
154158
"label": "Zod",
155159
"to": "framework/vue/examples/zod"
160+
},
161+
{
162+
"label": "Valibot",
163+
"to": "framework/vue/examples/valibot"
156164
}
157165
]
158166
}
@@ -205,6 +213,10 @@
205213
{
206214
"label": "Zod",
207215
"to": "framework/solid/examples/zod"
216+
},
217+
{
218+
"label": "Valibot",
219+
"to": "framework/solid/examples/valibot"
208220
}
209221
]
210222
}

docs/guides/basic-concepts.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Example:
109109

110110
## Validation Adapters
111111

112-
In addition to hand-rolled validation options, we also provide adapters like `@tanstack/zod-form-adapter` and `@tanstack/yup-form-adapter` to enable usage with common schema validation tools like [Yup](https://github.com/jquense/yup) and [Zod](https://zod.dev/).
112+
In addition to hand-rolled validation options, we also provide adapters like `@tanstack/zod-form-adapter`, `@tanstack/yup-form-adapter`, and `@tanstack/valibot-form-adapter` to enable usage with common schema validation tools like [Zod](https://zod.dev/), [Yup](https://github.com/jquense/yup), and [Valibot](https://valibot.dev/).
113113

114114
Example:
115115

docs/guides/validation.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -246,16 +246,18 @@ This will debounce every async call with a 500ms delay. You can even override th
246246
> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms.
247247
248248

249-
## Adapter-Based Validation (Zod, Yup)
249+
## Adapter-Based Validation (Zod, Yup, Valibot)
250250

251-
While functions provide more flexibility and customization over your validation, they can be a bit verbose. To help solve this, there are libraries like [Yup](https://github.com/jquense/yup) and [Zod](https://zod.dev/) that provide schema-based validation to make shorthand and type-strict validation substantially easier.
251+
While functions provide more flexibility and customization over your validation, they can be a bit verbose. To help solve this, there are libraries like [Valibot](https://valibot.dev/), [Yup](https://github.com/jquense/yup), and [Zod](https://zod.dev/) that provide schema-based validation to make shorthand and type-strict validation substantially easier.
252252

253253
Luckily, we support both of these libraries through official adapters:
254254

255255
```bash
256256
$ npm install @tanstack/zod-form-adapter zod
257257
# or
258258
$ npm install @tanstack/yup-form-adapter yup
259+
# or
260+
$ npm install @tanstack/valibot-form-adapter valibot
259261
```
260262

261263
Once done, we can add the adapter to the `validator` property on the form or field:

docs/installation.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ In addition, we support both Zod and Yup as validators through official validato
2121
$ npm i @tanstack/zod-form-adapter zod
2222
# or
2323
$ npm i @tanstack/yup-form-adapter yup
24+
# or
25+
$ npm i @tanstack/valibot-form-adapter valibot
2426
```

examples/react/simple/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
},
1010
"dependencies": {
1111
"@tanstack/react-form": "0.8.1",
12-
"axios": "^0.26.1",
1312
"react": "^18.0.0",
1413
"react-dom": "^18.0.0",
1514
"@tanstack/form-core": "0.8.1",

examples/react/valibot/.eslintrc.cjs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @ts-check
2+
3+
/** @type {import('eslint').Linter.Config} */
4+
const config = {
5+
extends: ["plugin:react/recommended", "plugin:react-hooks/recommended"],
6+
parserOptions: {
7+
tsconfigRootDir: __dirname,
8+
project: "./tsconfig.json",
9+
},
10+
rules: {
11+
"react/no-children-prop": "off",
12+
},
13+
};
14+
15+
module.exports = config;

examples/react/valibot/.gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
pnpm-lock.yaml
15+
yarn.lock
16+
package-lock.json
17+
18+
# misc
19+
.DS_Store
20+
.env.local
21+
.env.development.local
22+
.env.test.local
23+
.env.production.local
24+
25+
npm-debug.log*
26+
yarn-debug.log*
27+
yarn-error.log*

examples/react/valibot/.prettierrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

examples/react/valibot/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install`
6+
- `npm run dev`

examples/react/valibot/index.html

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" type="image/svg+xml" href="/emblem-light.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
9+
<title>TanStack Form React Valibot Example App</title>
10+
</head>
11+
<body>
12+
<noscript>You need to enable JavaScript to run this app.</noscript>
13+
<div id="root"></div>
14+
<script type="module" src="/src/index.tsx"></script>
15+
</body>
16+
</html>

examples/react/valibot/package.json

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "@tanstack/form-example-react-valibot",
3+
"version": "0.0.1",
4+
"main": "src/index.jsx",
5+
"scripts": {
6+
"dev": "vite --port=3001",
7+
"build": "vite build",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"@tanstack/react-form": "0.7.2",
12+
"react": "^18.0.0",
13+
"react-dom": "^18.0.0",
14+
"valibot": "^0.20.1",
15+
"@tanstack/valibot-form-adapter": "0.7.2",
16+
"@tanstack/form-core": "0.7.2",
17+
"@tanstack/zod-form-adapter": "0.7.2",
18+
"@tanstack/vue-form": "0.7.2",
19+
"@tanstack/yup-form-adapter": "0.7.2",
20+
"@tanstack/solid-form": "0.7.2"
21+
},
22+
"devDependencies": {
23+
"@vitejs/plugin-react": "^4.0.4",
24+
"vite": "^4.4.9"
25+
},
26+
"browserslist": {
27+
"production": [
28+
">0.2%",
29+
"not dead",
30+
"not op_mini all"
31+
],
32+
"development": [
33+
"last 1 chrome version",
34+
"last 1 firefox version",
35+
"last 1 safari version"
36+
]
37+
},
38+
"nx": {
39+
"implicitDependencies": [
40+
"@tanstack/form-core",
41+
"@tanstack/react-form",
42+
"@tanstack/zod-form-adapter"
43+
],
44+
"targets": {
45+
"test:types": {
46+
"dependsOn": [
47+
"build"
48+
]
49+
}
50+
}
51+
}
52+
}
Loading

examples/react/valibot/src/index.tsx

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import * as React from "react";
2+
import { createRoot } from "react-dom/client";
3+
import { useForm } from "@tanstack/react-form";
4+
import { valibotValidator } from "@tanstack/valibot-form-adapter";
5+
import { string, minLength, stringAsync, PipeResult } from "valibot";
6+
import type { FieldApi } from "@tanstack/react-form";
7+
8+
function FieldInfo({ field }: { field: FieldApi<any, any, unknown, unknown> }) {
9+
return (
10+
<>
11+
{field.state.meta.touchedErrors ? (
12+
<em>{field.state.meta.touchedErrors}</em>
13+
) : null}
14+
{field.state.meta.isValidating ? "Validating..." : null}
15+
</>
16+
);
17+
}
18+
19+
export default function App() {
20+
const form = useForm({
21+
defaultValues: {
22+
firstName: "",
23+
lastName: "",
24+
},
25+
onSubmit: async (values) => {
26+
// Do something with form data
27+
console.log(values);
28+
},
29+
// Add a validator to support Valibot usage in Form and Field
30+
validator: valibotValidator,
31+
});
32+
33+
return (
34+
<div>
35+
<h1>Valibot Form Example</h1>
36+
<form.Provider>
37+
<form
38+
onSubmit={(e) => {
39+
e.preventDefault();
40+
e.stopPropagation();
41+
void form.handleSubmit();
42+
}}
43+
>
44+
<div>
45+
{/* A type-safe field component*/}
46+
<form.Field
47+
name="firstName"
48+
onChange={string([
49+
minLength(3, "First name must be at least 3 characters"),
50+
])}
51+
onChangeAsyncDebounceMs={500}
52+
onChangeAsync={stringAsync([
53+
async (value) => {
54+
await new Promise((resolve) => setTimeout(resolve, 1000));
55+
return (
56+
value.includes("error")
57+
? {
58+
issues: [
59+
{
60+
input: value,
61+
validation: "firstName",
62+
message: "No 'error' allowed in first name",
63+
},
64+
],
65+
}
66+
: { output: value }
67+
) as PipeResult<string>;
68+
},
69+
])}
70+
children={(field) => {
71+
// Avoid hasty abstractions. Render props are great!
72+
return (
73+
<>
74+
<label htmlFor={field.name}>First Name:</label>
75+
<input
76+
name={field.name}
77+
value={field.state.value}
78+
onBlur={field.handleBlur}
79+
onChange={(e) => field.handleChange(e.target.value)}
80+
/>
81+
<FieldInfo field={field} />
82+
</>
83+
);
84+
}}
85+
/>
86+
</div>
87+
<div>
88+
<form.Field
89+
name="lastName"
90+
children={(field) => (
91+
<>
92+
<label htmlFor={field.name}>Last Name:</label>
93+
<input
94+
name={field.name}
95+
value={field.state.value}
96+
onBlur={field.handleBlur}
97+
onChange={(e) => field.handleChange(e.target.value)}
98+
/>
99+
<FieldInfo field={field} />
100+
</>
101+
)}
102+
/>
103+
</div>
104+
<form.Subscribe
105+
selector={(state) => [state.canSubmit, state.isSubmitting]}
106+
children={([canSubmit, isSubmitting]) => (
107+
<button type="submit" disabled={!canSubmit}>
108+
{isSubmitting ? "..." : "Submit"}
109+
</button>
110+
)}
111+
/>
112+
</form>
113+
</form.Provider>
114+
</div>
115+
);
116+
}
117+
118+
const rootElement = document.getElementById("root")!;
119+
120+
createRoot(rootElement).render(<App />);

examples/react/valibot/tsconfig.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"jsx": "react",
4+
"noEmit": true,
5+
"lib": ["DOM", "DOM.Iterable", "ES2020"]
6+
}
7+
}

examples/react/yup/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
},
1010
"dependencies": {
1111
"@tanstack/react-form": "0.8.1",
12-
"axios": "^0.26.1",
1312
"react": "^18.0.0",
1413
"react-dom": "^18.0.0",
1514
"yup": "^1.3.2",

examples/react/zod/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
},
1010
"dependencies": {
1111
"@tanstack/react-form": "0.8.1",
12-
"axios": "^0.26.1",
1312
"react": "^18.0.0",
1413
"react-dom": "^18.0.0",
1514
"zod": "^3.21.4",

examples/solid/valibot/.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

0 commit comments

Comments
 (0)