--- 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 ( <> {/* ... */} ); }} /> ```