apps/mantine.dev/src/pages/form/validation.mdx
import { FormDemos } from '@docs/demos'; import { Layout } from '@/layout'; import { MDX_DATA } from '@/mdx';
export default Layout(MDX_DATA.formValidation);
To validate a form with a rules object, provide an object of functions which take the field value as an argument and return an error message (any React node) or null if the field is valid:
<Demo data={FormDemos.rulesValidation} />Each form rule receives the following arguments:
value – value of the fieldvalues – all form valuespath – field path, for example user.email or cart.0.pricesignal – an AbortSignal that is aborted when a newer validation supersedes the current one (useful for cancelling in-flight async requests)The path argument can be used to get information about the field location relative to other fields.
For example, you can get the index of an array element:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: { a: [{ b: 1 }, { b: 2 }] },
validate: {
a: {
b: (value, values, path) => (path === 'a.0.b' ? 'error' : null),
},
},
});
formRootRule is a special rule path that can be used to validate objects and arrays
alongside their nested fields. For example, it is useful when you want to capture
a list of values, validate each value individually, and then validate the list itself
to not be empty:
Another example is to validate an object's field combination:
<Demo data={FormDemos.rootRuleObject} />You can get all form values as the second rule function argument to perform field validation based on other form values. For example, you can validate that the password confirmation is the same as the password:
<Demo data={FormDemos.password} />Another approach to handle validation is to provide a function to validate.
The function takes form values as a single argument and should return an object that contains
errors of corresponding fields. If a field is valid or field validation is not required, you can either return null or simply omit it
from the validation results.
To validate all fields on change, set the validateInputOnChange option to true:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
validateInputOnChange: true,
});
<Demo data={FormDemos.liveValidation} demoProps={{ toggle: true }} />
You can also provide an array of field paths to validate only those values:
import { FORM_INDEX, useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
validateInputOnChange: [
'name',
'email',
`jobs.${FORM_INDEX}.title`,
],
});
<Demo data={FormDemos.liveFieldValidation} demoProps={{ toggle: true }} />
To validate all fields on blur, set the validateInputOnBlur option to true:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
validateInputOnBlur: true,
});
<Demo data={FormDemos.blurValidation} demoProps={{ toggle: true }} />
You can also provide an array of field paths to validate only those values:
import { FORM_INDEX, useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
validateInputOnBlur: ['name', 'email', `jobs.${FORM_INDEX}.title`],
});
<Demo data={FormDemos.blurFieldValidation} demoProps={{ toggle: true }} />
By default, the field error is cleared when the value changes. To change this, set clearInputErrorOnChange to false:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
clearInputErrorOnChange: false,
});
<Demo data={FormDemos.clearErrorOnChange} demoProps={{ toggle: true }} />
form.onSubmit accepts two arguments: the first argument is the handleSubmit function that will be called with form values when validation
was completed without errors. The second argument is the handleErrors function, which is called with the errors object when validation was completed with errors.
You can use the handleErrors function to perform certain actions when the user tries to submit the form without values.
For example, you can show a notification:
form.isValid performs form validation with the given validation functions, rules object, or schema, but unlike
form.validate, it does not set form.errors and just returns a boolean value indicating whether the form is valid.
If any of the validation rules are async, form.isValid returns a Promise<boolean> instead.
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: '', age: 0 },
validate: {
name: (value) => (value.trim().length < 2 ? 'Too short' : null),
age: (value) => (value < 18 ? 'Too young' : null),
},
});
// With sync rules, returns boolean directly
form.isValid(); // -> false
form.isValid('name'); // -> false
Validation rules can be async – return a Promise that resolves to an error message or null.
When all rules are synchronous, form.validate(), form.validateField() and form.isValid() return
their results directly (not wrapped in a Promise). When any rule is async, these methods return promises instead.
TypeScript infers the correct return type based on your validation rules, so you get precise types without manual annotations.
The form.validating property is true while any async validation is in progress, and form.isValidating(path) can be used to check individual fields.
The validating state is never set for forms with only synchronous rules.
Each rule receives an AbortSignal as the fourth argument. The signal is aborted when a newer validation
supersedes the current one, which you can use to cancel in-flight HTTP requests and avoid race conditions.
When using async validation with validateInputOnChange, you can set validateDebounce to avoid
firing an API call on every keystroke. The debounce applies only to field-level validation triggered
by validateInputOnChange and validateInputOnBlur – it does not affect explicit form.validate() calls
or form.onSubmit().
The second argument of the form.onSubmit function is a callback function that is called
with the errors object when form validation fails.
You can use this callback to focus the first invalid field or perform any other action.
To get the DOM node of any input, use form.getInputNode('path-to-field'). Note that
in order for this feature to work, you need to spread form.getInputProps('path-to-field') to
the input element.