Manage form state


@mantine/form package does not depend on any other libraries, you can use it with or without @mantine/core inputs:

Install with npm:

npm install @mantine/form

Install with yarn:

yarn add @mantine/form


import { TextInput, Checkbox, Button, Group, Box } from '@mantine/core';
import { useForm } from '@mantine/form';
function Demo() {
const form = useForm({
initialValues: {
email: '',
termsOfService: false,
validate: {
email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
return (
<Box sx={{ maxWidth: 300 }} mx="auto">
<form onSubmit={form.onSubmit((values) => console.log(values))}>
label="I agree to sell my privacy"
{...form.getInputProps('termsOfService', { type: 'checkbox' })}
<Group position="right" mt="md">
<Button type="submit">Submit</Button>

API overview

All examples below use the following example use-form hook.

import { useForm } from '@mantine/form';
const form = useForm({
initialValues: {
path: '',
path2: '',
user: {
firstName: 'John',
lastName: 'Doe',
fruits: [
{ name: 'Banana', available: true },
{ name: 'Orange', available: false },
accepted: false,


Form values guide

// get current form values
// Set all form values
// Set all form values using the previous state
form.setValues((prev) => ({ ...prev, ...values }));
// Set value of single field
form.setFieldValue('path', value);
// Set value of nested field
form.setFieldValue('user.firstName', 'Jane');
// Resets form.values to initialValues,
// clears all validation errors,
// resets touched and dirty state

List items

Nested fields guide

// Inserts given list item at the specified path
form.insertListItem('fruits', { name: 'Apple', available: true });
// An optional index may be provided to specify the position in a nested field.
// If the index is passed where an item already exists, it will be replaced.
// If the index is larger than the current list, the element is inserted at the last position.
form.insertListItem('fruits', { name: 'Orange', available: true }, 1);
// Removes the list item at the specified path and index.
form.removeListItem('fruits', 1);
// Swaps two items of the list at the specified path.
// If no element exists at the `from` or `to` index, the list doesn't change.
form.reorderListItem('fruits', { from: 1, to: 0 });


Form validation guide

import { useForm } from '@mantine/form';
const form = useForm({
initialValues: {
email: '',
user: {
firstName: '',
lastName: '',
validate: {
email: (value) => (value.length < 2 ? 'Invalid email' : null),
user: {
firstName: (value) => (value.length < 2 ? 'First name must have at least 2 letters' : null),
// Validates all fields with specified `validate` function or schema, sets form.errors
// Validates single field at specified path, sets form.errors
// Works the same way as form.validate but does not set form.errors


Form errors guide

Validation errors occur when defined validation rules were violated, initialErrors were specified in useForm properties or validation errors were set manually.

// get current errors state
// Set all errors
form.setErrors({ path: 'Error message', path2: 'Another error' });
// Set error message at specified path
form.setFieldError('user.lastName', 'No special characters allowed');
// Clears all errors
// Clears error of field at specified path

onReset and onSubmit

Wrapper function for form onSubmit and onReset event handler. onSubmit handler accepts as second argument a function that will be called with errors object when validation fails.

<form onSubmit={form.onSubmit(setFormValues)}></form>
<form onSubmit={form.onSubmit(
(values, _event) => { setFormValues(values) },
(validationErrors, _values, _event) => { console.log(validationErrors) }
<form onReset={form.onReset}></form>

Touched and dirty

Touched & dirty guide

// Returns true if user interacted with any field inside form in any way
// Returns true if user interacted with field at specified path
// Set all touched values
form.setTouched({ 'user.firstName': true, 'user.lastName': false });
// Clears touched status of all fields
// Returns true if form values are not deep equal to initialValues
// Returns true if field value is not deep equal to initialValues
// Sets dirty status of all fields
form.setDirty({ 'user.firstName': true, 'user.lastName': false });
// Clears dirty status of all fields, saves form.values snapshot
// After form.resetDirty is called, form.isDirty will compare
// form.values to snapshot instead of initialValues


form.getInputProps returns an object with value, onChange, onFocus, onBlur and error that should be spread on input.

As second parameter options can be passed.

  • type: default input. Needs to be configured to checkbox if input requires checked to be set instead of value.
  • withError: default type === 'input'. Specifies if the returned object contains an error property with form.errors[path] value.
  • withFocus: default true. Specifies if the returned object contains an onFocus handler. If disabled, the touched state of the form can only be used if all values are set with setFieldValue.
<TextInput {...form.getInputProps('path')} />
<Checkbox {...form.getInputProps('accepted', { type: 'checkbox' })} />


UseFormReturnType can be used when you want to pass form as a prop to another component:

import { TextInput } from '@mantine/core';
import { useForm, UseFormReturnType } from '@mantine/form';
interface FormValues {
name: string;
occupation: string;
function NameInput({ form }: { form: UseFormReturnType<FormValues> }) {
return <TextInput {...form.getInputProps('name')} />;
function OccupationInput({ form }: { form: UseFormReturnType<FormValues> }) {
return <TextInput {...form.getInputProps('occupation')} />;
function Demo() {
const form = useForm<FormValues>({ initialValues: { name: '', occupation: '' } });
return (
<NameInput form={form} />
<OccupationInput form={form} />