---
id: form-validation
title: Form and Field Validation
---
# Form and Field Validation
At the core of TanStack Form's functionalities is the concept of validation. We currently support three mechanisms of validation:
- Synchronous functional validation
- Asynchronous functional validation
- Adapter-based validation
Let's take a look at each and see how they're built.
## Synchronous Functional Validation
With Form, you can pass a function to a field and, if it returns a string, said string will be used as the error:
```tsx
val < 13 ? "You must be 13 to make an account" : undefined}
children={(field) => {
return (
<>
field.handleChange(e.target.valueAsNumber)}
/>
{field.state.meta.touchedErrors ? (
{field.state.meta.touchedErrors}
) : null}
>
);
}}
/>
```
### Displaying Errors
Once you have your validation in place, you can map the errors from an array to be displayed in your UI:
```tsx
val < 13 ? "You must be 13 to make an account" : undefined}
children={(field) => {
return (
<>
{/* ... */}
{field.state.meta.errors ? (
{field.state.meta.errors}
) : null}
>
);
}}
/>
```
Or use the `errorMap` property to access the specific error you're looking for:
```tsx
val < 13 ? "You must be 13 to make an account" : undefined}
children={(field) => {
return (
<>
{/* ... */}
{field.state.meta.errorMap['onChange'] ? (
{field.state.meta.errorMap['onChange']}
) : null}
>
);
}}
/>
```
### Using Alternative Validation Steps
One of the great benefits of using TanStack Form is that you're not locked into a specific method of validation. For example, if you want to validate a specific field on blur rather than on text change, you can change `onChange` to `onBlur`:
```tsx
val < 13 ? "You must be 13 to make an account" : undefined}
children={(field) => {
return (
<>
{/* ... */}
>
);
}}
/>
```
## Asynchronous Functional Validation
While we suspect most validations will be synchronous, there's many instances where a network call or some other async operation would be useful to validate against.
To do this, we have dedicated `onChangeAsync`, `onBlurAsync`, and other methods that can be used to validate against:
```tsx
{
await new Promise((resolve) => setTimeout(resolve, 1000));
return (
value.includes("error") && 'No "error" allowed in first name'
);
}}
children={(field) => {
return (
<>
field.handleChange(e.target.value)}
/>
>
);
}}
/>
```
This can be combined with the respective synchronous properties as well:
``` tsx
!value
? "A first name is required"
: value.length < 3
? "First name must be at least 3 characters"
: undefined
}
onChangeAsync={async (value) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return (
value.includes("error") && 'No "error" allowed in first name'
);
}}
children={(field) => {
return (
<>
{/* ... */}
>
);
}}
/>
```
### Built-in Debouncing
While async calls are the way to go when validating against the database, running a network request on every keystroke is a good way to DDOS your database.
Instead, we enable an easy method for debouncing your `async` calls by adding a single property:
```tsx
{
// ...
}}
children={(field) => {
return (
<>
{/* ... */}
>
);
}}
/>
```
This will debounce every async call with a 500ms delay. You can even override this property on a per-validation property:
```tsx
{
// ...
}}
onBlurAsync={async (value) => {
// ...
}}
children={(field) => {
return (
<>
{/* ... */}
>
);
}}
/>
```
> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms.
## Adapter-Based Validation
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.
Luckily, we support both of these libraries through official adapters:
```bash
$ npm install @tanstack/zod-form-adapter zod
# or
$ 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";
// ...
const form = useForm({
// Either add the validator here or on `Field`
validator: zodValidator,
// ...
});
{
return (
<>
{/* ... */}
>
);
}}
/>
```
These adapters also support async operations using the proper property names:
```tsx
{
await new Promise((resolve) => setTimeout(resolve, 1000));
return !value.includes("error");
},
{
message: "No 'error' allowed in first name",
},
)}
children={(field) => {
return (
<>
{/* ... */}
>
);
}}
/>
```