Skip to content

Commit

Permalink
feat: native support for standard schema libraries (#1020)
Browse files Browse the repository at this point in the history
* feat: naive implementation as adapter

* test: naive test with valibot

* feat: add async validation support

* feat: define standard schema adapter in form-core

* feat: support arktype's function-based structure and path

* ci: apply automated fixes and generate docs

* feat: validate form with standard schema without adapter

* feat: validate field with standard schema without adapter

* test: add todo tests for zod

* ci: apply automated fixes and generate docs

* fix: spread field errors from form validator

* docs: add example with multiple schemas

* chore: bump valibot and uncomment failing tests

* docs: mention standard schema

* feat: accept functional sync validator even with an adapter set

* chore: bump versions

* docs: gen

* ci: apply automated fixes and generate docs

* feat: integrate zod

* docs: update with zod and simplify examples

* ci: apply automated fixes and generate docs

* docs: mention latest version for schema libraries
  • Loading branch information
Balastrong authored Dec 10, 2024
1 parent b437abe commit 6a82b7f
Show file tree
Hide file tree
Showing 52 changed files with 1,663 additions and 520 deletions.
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@
"label": "TanStack Query Integration",
"to": "framework/react/examples/query-integration"
},
{
"label": "Standard Schema",
"to": "framework/react/examples/standard-schema"
},
{
"label": "Yup",
"to": "framework/react/examples/yup"
Expand Down
88 changes: 49 additions & 39 deletions docs/framework/angular/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,24 +382,22 @@ This will debounce every async call with a 500ms delay. You can even override th

> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms.
## Adapter-Based Validation (Zod, Yup, Valibot)
## Validation through Schema Libraries

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.
While functions provide more flexibility and customization over your validation, they can be a bit verbose. To help solve this, there are libraries that provide schema-based validation to make shorthand and type-strict validation substantially easier. You can also define a single schema for your entire form and pass it to the form level, errors will be automatically propagated to the fields.

Luckily, we support all of these libraries through official adapters:
### Standard Schema Libraries

```bash
$ npm install @tanstack/zod-form-adapter zod
# or
$ npm install @tanstack/yup-form-adapter yup
# or
$ npm install @tanstack/valibot-form-adapter valibot
```
TanStack Form natively supports all libraries following the [Standard Schema specification](https://github.com/standard-schema/standard-schema), most notably:
- [Zod](https://zod.dev/)
- [Valibot](https://valibot.dev/)
- [ArkType](https://arktype.io/)

Once done, we can add the adapter to the `validator` property on the form or field:
*Note:* make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet.

To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function:

```angular-ts
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'
@Component({
Expand All @@ -420,19 +418,18 @@ import { z } from 'zod'
`,
})
export class AppComponent {
form = injectForm({
// Either add the validator here or on `Field`
validatorAdapter: zodValidator(),
form = injectForm({
// ...
})
})
z = z
// ...
}
```

These adapters also support async operations using the proper property names:
Async validations on form and field level are supported as well:


```angular-ts
@Component({
Expand Down Expand Up @@ -469,37 +466,50 @@ export class AppComponent {
}
```

### Form Level Adapter Validation
### Other Schema Libraries

You can also use the adapter at the form level:
We also support [Yup](https://github.com/jquense/yup) through an official adapter:

```typescript
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'
```bash
$ npm install @tanstack/yup-form-adapter yup
```

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

const form = injectForm({
validatorAdapter: zodValidator(),
validators: {
onChange: z.object({
age: z.number().gte(13, 'You must be 13 to make an account'),
}),
},
})
```
```angular-ts
import { yupValidator } from '@tanstack/yup-form-adapter'
import * as yup from 'yup'
If you use the adapter at the form level, it will pass the validation to the fields of the same name.
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="age"
[validators]="{
onChange: yup.number().moreThan(13, 'You must be 13 to make an account'),
}"
#age="field"
>
<!-- ... -->
</ng-container>
`,
})
export class AppComponent {
form = injectForm({
// Either add the validator here or on `Field`
validatorAdapter: yupValidator(),
// ...
})
This means that:
yup = yup
```html
<ng-container [tanstackField]="form" name="age" #age="field">
<!-- ... -->
</ng-container>
// ...
}
```

Will still display the error message from the form-level validation.

## Preventing invalid forms from being submitted

Expand Down
6 changes: 3 additions & 3 deletions docs/framework/react/guides/ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This section focuses on integrating TanStack Form with TanStack Start.

- Start a new `TanStack Start` project, following the steps in the [TanStack Start Quickstart Guide](https://tanstack.com/router/latest/docs/framework/react/guide/tanstack-start)
- Install `@tanstack/react-form`
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#adapter-based-validation-zod-yup-valibot) of your choice. [Optional]
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#validation-through-schema-libraries) of your choice. [Optional]

### Start integration

Expand Down Expand Up @@ -169,7 +169,7 @@ This section focuses on integrating TanStack Form with `Next.js`, particularly u

- Start a new `Next.js` project, following the steps in the [Next.js Documentation](https://nextjs.org/docs/getting-started/installation). Ensure you select `yes` for `Would you like to use App Router?` during the setup to access all new features provided by Next.js.
- Install `@tanstack/react-form`
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#adapter-based-validation-zod-yup-valibot) of your choice. [Optional]
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#validation-through-schema-libraries) of your choice. [Optional]

## App Router integration

Expand Down Expand Up @@ -315,7 +315,7 @@ Here, we're using [React's `useActionState` hook](https://unicorn-utterances.com
- Start a new `Remix` project, following the steps in the [Remix Documentation](https://remix.run/docs/en/main/start/quickstart).
- Install `@tanstack/react-form`
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#adapter-based-validation-zod-yup-valibot) of your choice. [Optional]
- Install any [form validator](/form/latest/docs/framework/react/guides/validation#validation-through-schema-libraries) of your choice. [Optional]
## Remix integration
Expand Down
102 changes: 50 additions & 52 deletions docs/framework/react/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,47 +413,49 @@ This will debounce every async call with a 500ms delay. You can even override th
> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms.
## Adapter-Based Validation (Zod, Yup, Valibot)
## Validation through Schema Libraries
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.
While functions provide more flexibility and customization over your validation, they can be a bit verbose. To help solve this, there are libraries that provide schema-based validation to make shorthand and type-strict validation substantially easier. You can also define a single schema for your entire form and pass it to the form level, errors will be automatically propagated to the fields.
Luckily, we support all of these libraries through official adapters:
### Standard Schema Libraries
```bash
$ npm install @tanstack/zod-form-adapter zod
# or
$ npm install @tanstack/yup-form-adapter yup
# or
$ npm install @tanstack/valibot-form-adapter valibot
```
TanStack Form natively supports all libraries following the [Standard Schema specification](https://github.com/standard-schema/standard-schema), most notably:
- [Zod](https://zod.dev/)
- [Valibot](https://valibot.dev/)
- [ArkType](https://arktype.io/)
Once done, we can add the adapter to the `validator` property on the form or field:
*Note:* make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet.
```tsx
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'
// ...
To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function:
const form = useForm({
// Either add the validator here or on `Field`
validatorAdapter: zodValidator(),
// ...
```tsx
const userSchema = z.object({
age: z.number().gte(13, 'You must be 13 to make an account'),
})
<form.Field
name="age"
validatorAdapter={zodValidator()}
validators={{
onChange: z.number().gte(13, 'You must be 13 to make an account'),
}}
children={(field) => {
return <>{/* ... */}</>
}}
/>
function App() {
const form = useForm({
defaultValues: {
age: 0,
},
validators: {
onChange: userSchema,
},
})
return (
<div>
<form.Field
name="age"
children={(field) => {
return <>{/* ... */}</>
}}
/>
</div>
)
}
```
These adapters also support async operations using the proper property names:
Async validations on form and field level are supported as well:
```tsx
<form.Field
Expand All @@ -477,44 +479,40 @@ These adapters also support async operations using the proper property names:
/>
```
### Form Level Adapter Validation
### Other Schema Libraries
We also support [Yup](https://github.com/jquense/yup) through an official adapter:
You can also use the adapter at the form level:
```bash
$ npm install @tanstack/yup-form-adapter yup
```
Once done, we can add the adapter to the `validator` property on the form or field:
```tsx
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'
import { yupValidator } from '@tanstack/yup-form-adapter'
import * as yup from 'yup'
// ...
const formSchema = z.object({
age: z.number().gte(13, 'You must be 13 to make an account'),
})
const form = useForm({
validatorAdapter: zodValidator(),
validators: {
onChange: formSchema
},
// Either add the validator here or on `Field`
validatorAdapter: yupValidator(),
// ...
})
```
If you use the adapter at the form level, it will pass the validation to the fields of the same name.
This means that:
```tsx
<form.Field
name="age"
validatorAdapter={yupValidator()}
validators={{
onChange: yup.number().moreThan(13, 'You must be 13 to make an account'),
}}
children={(field) => {
return <>{/* ... */}</>
}}
/>
```
Will still display the error message from the form-level validation.
## Preventing invalid forms from being submitted
The `onChange`, `onBlur` etc... callbacks are also run when the form is submitted and the submission is blocked if the form is invalid.
Expand Down
Loading

0 comments on commit 6a82b7f

Please sign in to comment.