validateInputOnBlur

use-form now supports validateInputOnBlur option, it works similar to validateInputOnChange:

import { useForm } from '@mantine/form';
import { NumberInput, TextInput, Button } from '@mantine/core';
function Demo() {
const form = useForm({
validateInputOnBlur: ['name', 'email'],
initialValues: { name: '', email: '', age: 0 },
// functions will be used to validate values at corresponding key
validate: {
name: (value) => (value.length < 2 ? 'Name must have at least 2 letters' : null),
email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
age: (value) => (value < 18 ? 'You must be at least 18 to register' : null),
},
});
return (
<form onSubmit={form.onSubmit(console.log)}>
<TextInput label="Name" placeholder="Name" {...form.getInputProps('name')} />
<TextInput mt="sm" label="Email" placeholder="Email" {...form.getInputProps('email')} />
<NumberInput
mt="sm"
label="Age"
placeholder="Age"
min={0}
max={99}
{...form.getInputProps('age')}
/>
<Button type="submit" mt="sm">
Submit
</Button>
</form>
);
}

Non-linear Slider scale

Slider and RangeSlider components now support non-linear scale:

1 MB
1 MB
1 GB
import { RangeSlider, Slider, Stack } from '@mantine/core';
function Demo() {
function valueLabelFormat(value: number) {
const units = ['KB', 'MB', 'GB', 'TB'];
let unitIndex = 0;
let scaledValue = value;
while (scaledValue >= 1024 && unitIndex < units.length - 1) {
unitIndex += 1;
scaledValue /= 1024;
}
return `${scaledValue} ${units[unitIndex]}`;
}
return (
<Stack spacing="xl" p="xl">
<Slider
py="xl"
scale={(v) => 2 ** v}
step={1}
min={2}
max={30}
labelAlwaysOn
defaultValue={10}
label={valueLabelFormat}
/>
<RangeSlider
py="xl"
scale={(v) => 2 ** v}
step={1}
min={2}
max={30}
labelAlwaysOn
defaultValue={[10, 20]}
label={valueLabelFormat}
/>
</Stack>
);
}

Switch.Group component

New Switch.Group component lets you organize Switch components the same way as Checkbox.Group and Radio.Group:

Select your favorite framework/library
This is anonymous
Orientation
Spacing
xs
sm
md
lg
xl
Offset
xs
sm
md
lg
xl
Size
xs
sm
md
lg
xl
import { Switch } from '@mantine/core';
function Demo() {
return (
<Switch.Group
defaultValue={['react']}
label="Select your favorite framework/library"
description="This is anonymous"
withAsterisk
>
<Switch value="react" label="React" />
<Switch value="svelte" label="Svelte" />
<Switch value="ng" label="Angular" />
<Switch value="vue" label="Vue" />
</Switch.Group>
);
}

Controlled Select/MultiSelect search value

Select and MultiSelect search value can now be controlled:

import { Select } from '@mantine/core';
function Demo() {
const [searchValue, onSearchChange] = useState('');
return (
<Select
label="Your favorite framework/library"
placeholder="Pick one"
searchable
onSearchChange={onSearchChange}
searchValue={searchValue}
nothingFound="No options"
data={['React', 'Angular', 'Svelte', 'Vue']}
/>
);
}

Controlled PasswordInput visibility

PasswordInput now supports controlled visibility state with visible and onVisibilityChange props, for example, the props can be used to sync visibility state between two inputs:

import { useDisclosure } from '@mantine/hooks';
import { PasswordInput, Stack } from '@mantine/core';
function Demo() {
const [visible, { toggle }] = useDisclosure(false);
return (
<Stack sx={{ maxWidth: 380 }} mx="auto">
<PasswordInput
label="Password"
defaultValue="secret"
visible={visible}
onVisibilityChange={toggle}
/>
<PasswordInput
label="Confirm password"
defaultValue="secret"
visible={visible}
onVisibilityChange={toggle}
/>
</Stack>
);
}

New Mantine UI components

10 new components were added to Mantine UI, view changelog here

Other changes