Recipes

use-form usage examples

Set initial values with async request

import { useEffect } from 'react';
import { useForm } from '@mantine/form';
import { TextInput, Checkbox } from '@mantine/core';
interface FormValues {
email: string;
terms: boolean;
}
function loadInitialValues(): Promise<FormValues> {
return new Promise((resolve) => {
setTimeout(() => resolve({ email: 'test@email', terms: true }), 2000);
});
}
function Demo() {
const form = useForm<FormValues>({ initialValues: { email: '', terms: false } });
useEffect(() => {
loadInitialValues().then((values) => {
form.setValues(values);
form.resetDirty(values);
});
}, []);
return (
<div style={{ maxWidth: 320, margin: 'auto' }}>
<TextInput label="Email" placeholder="Email" {...form.getInputProps('email')} />
<Checkbox
mt="sm"
label="I accept terms and conditions"
{...form.getInputProps('terms', { type: 'checkbox' })}
/>
</div>
);
}

Save form values to local storage

import { useEffect } from 'react';
import { useForm } from '@mantine/form';
import { TextInput } from '@mantine/core';
function Demo() {
const form = useForm({ initialValues: { name: '', occupation: '' } });
useEffect(() => {
const storedValue = window.localStorage.getItem('user-form');
if (storedValue) {
try {
form.setValues(JSON.parse(window.localStorage.getItem('user-form')));
} catch (e) {
console.log('Failed to parse stored value');
}
}
}, []);
useEffect(() => {
window.localStorage.setItem('user-form', JSON.stringify(form.values));
}, [form.values]);
return (
<div style={{ maxWidth: 320, margin: 'auto' }}>
<TextInput label="Name" placeholder="Name" {...form.getInputProps('name')} />
<TextInput
mt="md"
label="Occupation"
placeholder="Occupation"
{...form.getInputProps('occupation')}
/>
</div>
);
}

List items reordering

Form values:
{
  "employees": [
    {
      "name": "John Doe",
      "email": "john@mantine.dev"
    },
    {
      "name": "Bill Love",
      "email": "bill@mantine.dev"
    },
    {
      "name": "Nancy Eagle",
      "email": "nanacy@mantine.dev"
    },
    {
      "name": "Lim Notch",
      "email": "lim@mantine.dev"
    },
    {
      "name": "Susan Seven",
      "email": "susan@mantine.dev"
    }
  ]
}
import { Group, TextInput, Box, Text, Code, Button, Center } from '@mantine/core';
import { useForm } from '@mantine/form';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { IconGripVertical } from '@tabler/icons';
function Demo() {
const form = useForm({
initialValues: {
employees: [
{ name: 'John Doe', email: 'john@mantine.dev' },
{ name: 'Bill Love', email: 'bill@mantine.dev' },
{ name: 'Nancy Eagle', email: 'nanacy@mantine.dev' },
{ name: 'Lim Notch', email: 'lim@mantine.dev' },
{ name: 'Susan Seven', email: 'susan@mantine.dev' },
],
},
});
const fields = form.values.employees.map((_, index) => (
<Draggable key={index} index={index} draggableId={index.toString()}>
{(provided) => (
<Group ref={provided.innerRef} mt="xs" {...provided.draggableProps}>
<Center {...provided.dragHandleProps}>
<IconGripVertical size={18} />
</Center>
<TextInput placeholder="John Doe" {...form.getInputProps(`employees.${index}.name`)} />
<TextInput
placeholder="example@mail.com"
{...form.getInputProps(`employees.${index}.email`)}
/>
</Group>
)}
</Draggable>
));
return (
<Box sx={{ maxWidth: 500 }} mx="auto">
<DragDropContext
onDragEnd={({ destination, source }) =>
form.reorderListItem('employees', { from: source.index, to: destination.index })
}
>
<Droppable droppableId="dnd-list" direction="vertical">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{fields}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<Group position="center" mt="md">
<Button onClick={() => form.insertListItem('employees', { name: '', email: '' })}>
Add employee
</Button>
</Group>
<Text size="sm" weight={500} mt="md">
Form values:
</Text>
<Code block>{JSON.stringify(form.values, null, 2)}</Code>
</Box>
);
}

Form with multiple steps

import { useState } from 'react';
import { Stepper, Button, Group, TextInput, PasswordInput, Code } from '@mantine/core';
import { useForm } from '@mantine/form';
function Demo() {
const [active, setActive] = useState(0);
const form = useForm({
initialValues: {
username: '',
password: '',
name: '',
email: '',
website: '',
github: '',
},
validate: (values) => {
if (active === 0) {
return {
username:
values.username.trim().length < 6
? 'Username must include at least 6 characters'
: null,
password:
values.password.length < 6 ? 'Password must include at least 6 characters' : null,
};
}
if (active === 1) {
return {
name: values.name.trim().length < 2 ? 'Name must include at least 2 characters' : null,
email: /^\S+@\S+$/.test(values.email) ? null : 'Invalid email',
};
}
return {};
},
});
const nextStep = () =>
setActive((current) => {
if (form.validate().hasErrors) {
return current;
}
return current < 3 ? current + 1 : current;
});
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
return (
<>
<Stepper active={active} breakpoint="sm">
<Stepper.Step label="First step" description="Profile settings">
<TextInput label="Username" placeholder="Username" {...form.getInputProps('username')} />
<PasswordInput
mt="md"
label="Password"
placeholder="Password"
{...form.getInputProps('password')}
/>
</Stepper.Step>
<Stepper.Step label="Second step" description="Personal information">
<TextInput label="Name" placeholder="Name" {...form.getInputProps('name')} />
<TextInput mt="md" label="Email" placeholder="Email" {...form.getInputProps('email')} />
</Stepper.Step>
<Stepper.Step label="Final step" description="Social media">
<TextInput label="Website" placeholder="Website" {...form.getInputProps('website')} />
<TextInput
mt="md"
label="GitHub"
placeholder="GitHub"
{...form.getInputProps('github')}
/>
</Stepper.Step>
<Stepper.Completed>
Completed! Form values:
<Code block mt="xl">
{JSON.stringify(form.values, null, 2)}
</Code>
</Stepper.Completed>
</Stepper>
<Group position="right" mt="xl">
{active !== 0 && (
<Button variant="default" onClick={prevStep}>
Back
</Button>
)}
{active !== 3 && <Button onClick={nextStep}>Next step</Button>}
</Group>
</>
);
}