Nested fields
Manage nested arrays and object state with use-form hook
Docs
Package
Properties paths
Most of form
handlers accept property path as the first argument.
Property path includes keys/indices of objects/arrays at which target property is contained:
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: {
user: {
firstName: 'John',
lastName: 'Doe',
},
fruits: [
{ name: 'Banana', available: true },
{ name: 'Orange', available: false },
],
deeply: {
nested: {
object: [{ item: 1 }, { item: 2 }],
},
},
},
});
// Props for input that is controlled by user object firstName field
form.getInputProps('user.firstName');
// Set value of `name` field that is contained in object at second position of fruits array:
form.setFieldValue('fruits.1.name', 'Carrot');
// Validate deeply nested field
form.validateField('deeply.nested.object.0.item');
Nested objects
import { useForm } from '@mantine/form';
import { Box, Checkbox, TextInput } from '@mantine/core';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: {
terms: false,
user: {
firstName: '',
lastName: '',
},
},
});
return (
<Box maw={340} mx="auto">
<TextInput
label="First name"
placeholder="First name"
key={form.key('user.firstName')}
{...form.getInputProps('user.firstName')}
/>
<TextInput
label="Last name"
placeholder="Last name"
mt="md"
key={form.key('user.lastName')}
{...form.getInputProps('user.lastName')}
/>
<Checkbox
label="I accepts terms & conditions"
mt="sm"
key={form.key('terms')}
{...form.getInputProps('terms', { type: 'checkbox' })}
/>
</Box>
);
}
Set nested object value
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: {
user: {
name: '',
occupation: '',
},
},
});
// You can set values for each field individually
form.setFieldValue('user.name', 'John');
form.setFieldValue('user.occupation', 'Engineer');
// Or set the entire object
form.setFieldValue('user', { name: 'Jane', occupation: 'Architect' });
Nested object values validation
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: {
user: {
name: '',
occupation: '',
},
},
validate: {
user: {
name: (value) =>
value.length < 2 ? 'Name is too short' : null,
occupation: (value) =>
value.length < 2 ? 'Occupation is too short' : null,
},
},
});
form.validate();
form.errors; // -> { 'user.name': 'Name is too short', 'user.occupation': 'Occupation is too short' }
Nested arrays
Name
Status
import { useForm } from '@mantine/form';
import { TextInput, Switch, Group, ActionIcon, Box, Text, Button } from '@mantine/core';
import { randomId } from '@mantine/hooks';
import { IconTrash } from '@tabler/icons-react';
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: {
employees: [{ name: '', active: false, key: randomId() }],
},
});
const fields = form.getValues().employees.map((item, index) => (
<Group key={item.key} mt="xs">
<TextInput
placeholder="John Doe"
withAsterisk
style={{ flex: 1 }}
key={form.key(`employees.${index}.name`)}
{...form.getInputProps(`employees.${index}.name`)}
/>
<Switch
label="Active"
key={form.key(`employees.${index}.active`)}
{...form.getInputProps(`employees.${index}.active`, { type: 'checkbox' })}
/>
<ActionIcon color="red" onClick={() => form.removeListItem('employees', index)}>
<IconTrash size="1rem" />
</ActionIcon>
</Group>
));
return (
<Box maw={500} mx="auto">
{fields.length > 0 ? (
<Group mb="xs">
<Text fw={500} size="sm" style={{ flex: 1 }}>
Name
</Text>
<Text fw={500} size="sm" pr={90}>
Status
</Text>
</Group>
) : (
<Text c="dimmed" ta="center">
No one here...
</Text>
)}
{fields}
<Group justify="center" mt="md">
<Button
onClick={() =>
form.insertListItem('employees', { name: '', active: false, key: randomId() })
}
>
Add employee
</Button>
</Group>
</Box>
);
}
List handlers
useForm
hook provides the following handlers to manage list state:
removeListItem
– removes list item at given indexinsertListItem
– inserts list item at given index (appends item to the end of the list if index is not specified)reorderListItem
– reorders list item with given position at specified fieldreplaceListItem
– replaces list item at given index with new value
List values validation
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
initialValues: {
users: [
{ name: 'John', age: 12 },
{ name: '', age: 22 },
],
},
validate: {
users: {
name: (value) =>
value.length < 2
? 'Name should have at least 2 letters'
: null,
age: (value) =>
value < 18 ? 'User must be 18 or older' : null,
},
},
});
// Validate list item field
form.validateField('users.1.name');
// Or with all other fields
form.validate();
console.log(form.errors);
// {
// 'users.0.age': 'User must be 18 or older',
// 'users.1.name': 'Name should have at least 2 letters'
// }
Welcome to Mantine, React components library that you always wished for
Build fully functional accessible web applications faster than ever