use-form

Manage form state

License

Installation

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

yarn add @mantine/form

Usage

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 maw={340} mx="auto">
      <form onSubmit={form.onSubmit((values) => console.log(values))}>
        <TextInput
          withAsterisk
          label="Email"
          placeholder="your@email.com"
          {...form.getInputProps('email')}
        />

        <Checkbox
          mt="md"
          label="I agree to sell my privacy"
          {...form.getInputProps('termsOfService', { type: 'checkbox' })}
        />

        <Group justify="flex-end" mt="md">
          <Button type="submit">Submit</Button>
        </Group>
      </form>
    </Box>
  );
}

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,
  },
});

Values

Form values guide

// get current form values
form.values;

// Set all form values
form.setValues(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
form.reset();

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 provided, item will be inserted at the given position.
// 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.
// You should make sure that there are elements at at the `from` and `to` index.
form.reorderListItem('fruits', { from: 1, to: 0 });

Validation

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
form.validate();

// Validates single field at specified path, sets form.errors
form.validateField('user.firstName');

// Works the same way as form.validate but does not set form.errors
form.isValid();
form.isValid('user.firstName');

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
form.errors;

// 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
form.clearErrors();

// Clears error of field at specified path
form.clearFieldError('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>
<form onReset={form.onReset}></form>

Touched and dirty

Touched & dirty guide

// Returns true if user interacted with any field inside form in any way
form.isTouched();

// Returns true if user interacted with field at specified path
form.isTouched('path');

// Set all touched values
form.setTouched({ 'user.firstName': true, 'user.lastName': false });

// Clears touched status of all fields
form.resetTouched();

// Returns true if form values are not deep equal to initialValues
form.isDirty();

// Returns true if field value is not deep equal to initialValues
form.isDirty('path');

// 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.resetDirty();

getInputProps

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

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