Back to Mantine

Validation

apps/mantine.dev/src/pages/form/validation.mdx

9.3.17.1 KB
Original Source

import { FormDemos } from '@docs/demos'; import { Layout } from '@/layout'; import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.formValidation);

Validation with rules object

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} />

Rule function arguments

Each form rule receives the following arguments:

  • value – value of the field
  • values – all form values
  • path – field path, for example user.email or cart.0.price
  • signal – 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:

tsx
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

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:

<Demo data={FormDemos.rootRuleArray} />

Another example is to validate an object's field combination:

<Demo data={FormDemos.rootRuleObject} />

Validation based on other form values

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} />

Function-based validation

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.

<Demo data={FormDemos.validateFunction} />

Validate fields on change

To validate all fields on change, set the validateInputOnChange option to true:

tsx
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:

tsx
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 }} />

Validate fields on blur

To validate all fields on blur, set the validateInputOnBlur option to true:

tsx
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:

tsx
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 }} />

Clear field error on change

By default, the field error is cleared when the value changes. To change this, set clearInputErrorOnChange to false:

tsx
import { useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  clearInputErrorOnChange: false,
});

<Demo data={FormDemos.clearErrorOnChange} demoProps={{ toggle: true }} />

Validation in onSubmit handler

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:

<Demo data={FormDemos.onSubmitErrors} />

isValid handler

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.

tsx
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

Async validation

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.

<Demo data={FormDemos.asyncValidation} />

Async validation with debounce

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().

<Demo data={FormDemos.asyncValidationDebounce} />

Focus the first invalid field

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.

<Demo data={FormDemos.focusError} />