Rating component

New Rating component:

Color
Size
xs
sm
md
lg
xl
import { Rating } from '@mantine/core';
function Demo() {
return <Rating defaultValue={2} />
}

Progress sections props

Progress and RingProgress components now support adding props to sections:

Hovered section: none
Hovered section: none

Vertical tabs placement

Vertical Tabs now support placement prop:

Messages tab content
Settings tab content
Placement
import { Tabs } from '@mantine/core';
function Demo() {
return (
<Tabs defaultValue="gallery" orientation="vertical">
<Tabs.List>
<Tabs.Tab value="gallery">Gallery</Tabs.Tab>
<Tabs.Tab value="messages">Messages</Tabs.Tab>
<Tabs.Tab value="settings">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="gallery">Gallery tab content</Tabs.Panel>
<Tabs.Panel value="messages">Messages tab content</Tabs.Panel>
<Tabs.Panel value="settings">Settings tab content</Tabs.Panel>
</Tabs>
);
}

use-favicon hook

New use-favicon hook:

import { useState } from 'react';
import { useFavicon } from '@mantine/hooks';
import { Group, Button } from '@mantine/core';
function Demo() {
const [favicon, setFavicon] = useState('https://mantine.dev/favicon.svg');
const setTwitterFavicon = () => setFavicon('https://twitter.com/favicon.ico');
const setMantineFavicon = () => setFavicon('https://mantine.dev/favicon.svg');
useFavicon(favicon);
return (
<Group position="center">
<Button onClick={setTwitterFavicon}>Twitter favicon</Button>
<Button onClick={setMantineFavicon}>Mantine favicon</Button>
</Group>
);
}

Form index reference in validateInputOnBlur and validateInputOnChange

You can now use FORM_INDEX in use-form to validate nested array fields with validateInputOnBlur and validateInputOnChange settings:

import { useForm, FORM_INDEX } from '@mantine/form';
import { NumberInput, TextInput, Button } from '@mantine/core';
function Demo() {
const form = useForm({
validateInputOnChange: [
'email',
'name',
// use FORM_INDEX to reference fields indices
`jobs.${FORM_INDEX}.title`,
],
initialValues: { name: '', email: '', age: 0, jobs: [{ title: '' }, { title: '' }] },
// 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),
jobs: {
title: (value) => (value.length < 2 ? 'Job must have at least 2 letters' : null),
},
},
});
return (
<form style={{ maxWidth: 320, margin: 'auto' }} 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')}
/>
<TextInput
mt="sm"
label="Job 1"
placeholder="Job 1"
{...form.getInputProps('jobs.0.title')}
/>
<TextInput
mt="sm"
label="Job 2"
placeholder="Job 2"
{...form.getInputProps('jobs.1.title')}
/>
<Button type="submit" mt="sm">
Submit
</Button>
</form>
);
}

use-form transformValues

use-form now supports transformValues options, it transforms values before they get submitted in onSubmit handler. For example, it can be used to merge several fields into one or to convert types:

import { useState } from 'react';
import { useForm } from '@mantine/form';
import { TextInput, Button, Box, Code } from '@mantine/core';
function Demo() {
const [submittedValues, setSubmittedValues] = useState('');
const form = useForm({
initialValues: {
firstName: 'Jane',
lastName: 'Doe',
age: '33',
},
transformValues: (values) => ({
fullName: `${values.firstName} ${values.lastName}`,
age: Number(values.age) || 0,
}),
});
return (
<Box sx={{ maxWidth: 400 }} mx="auto">
<form
onSubmit={form.onSubmit((values) => setSubmittedValues(JSON.stringify(values, null, 2)))}
>
<TextInput
label="First name"
placeholder="First name"
{...form.getInputProps('firstName')}
/>
<TextInput
label="Last name"
placeholder="Last name"
mt="md"
{...form.getInputProps('lastName')}
/>
<TextInput
type="number"
label="Age"
placeholder="Age"
mt="md"
{...form.getInputProps('age')}
/>
<Button type="submit" mt="md">
Submit
</Button>
</form>
{submittedValues && <Code block>{submittedValues}</Code>}
</Box>
);
}

Other changes