View changelog with demos on mantine.dev website
Support Mantine development
You can now sponsor Mantine development with OpenCollective.
All funds will be used to improve Mantine and create new features and components.
use-radial-move hook
New use-radial-move hook can be used to create custom radial sliders:
import { useState } from 'react';
import { Box } from '@mantine/core';
import { useRadialMove } from '@mantine/hooks';
import classes from './Demo.module.css';
function Demo() {
const [value, setValue] = useState(115);
const { ref } = useRadialMove(setValue);
return (
<Box className={classes.root} ref={ref} style={{ '--angle': `${value}deg` }}>
<div className={classes.value}>{value}°</div>
<div className={classes.thumb} />
</Box>
);
}
BarChart color based on value
BarChart component now supports getBarColor
prop to assign color based on value.
getBarColor
function is called with two arguments: value and series object. It should return a color
string (theme color reference or any valid CSS color value).
import { BarChart } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<BarChart
h={300}
data={data}
dataKey="month"
getBarColor={(value) => (value > 700 ? 'teal.8' : 'red.8')}
series={[{ name: 'Laptops', color: 'gray.6' }]}
/>
);
}
Button.GroupSection and ActionIcon.GroupSection
ActionIcon.GroupSection
and Button.GroupSection
are new components that
can be used in ActionIcon.Group
/Button.Group
to create sections that are
not ActionIcon
/Button
components:
import { IconChevronDown, IconChevronUp } from '@tabler/icons-react';
import { ActionIcon } from '@mantine/core';
import { useCounter } from '@mantine/hooks';
function Demo() {
const [value, { increment, decrement }] = useCounter(135, { min: 0 });
return (
<ActionIcon.Group>
<ActionIcon variant="default" size="lg" radius="md" onClick={decrement}>
<IconChevronDown color="var(--mantine-color-red-text)" />
</ActionIcon>
<ActionIcon.GroupSection variant="default" size="lg" bg="var(--mantine-color-body)" miw={60}>
{value}
</ActionIcon.GroupSection>
<ActionIcon variant="default" size="lg" radius="md" onClick={increment}>
<IconChevronUp color="var(--mantine-color-teal-text)" />
</ActionIcon>
</ActionIcon.Group>
);
}
Table vertical variant
Table component now support variant="vertical"
:
import { Table } from '@mantine/core';
export function Demo() {
return (
<Table variant="vertical" layout="fixed" withTableBorder>
<Table.Tbody>
<Table.Tr>
<Table.Th w={160}>Epic name</Table.Th>
<Table.Td>7.x migration</Table.Td>
</Table.Tr>
<Table.Tr>
<Table.Th>Status</Table.Th>
<Table.Td>Open</Table.Td>
</Table.Tr>
<Table.Tr>
<Table.Th>Total issues</Table.Th>
<Table.Td>135</Table.Td>
</Table.Tr>
<Table.Tr>
<Table.Th>Total story points</Table.Th>
<Table.Td>874</Table.Td>
</Table.Tr>
<Table.Tr>
<Table.Th>Last updated at</Table.Th>
<Table.Td>September 26, 2024 17:41:26</Table.Td>
</Table.Tr>
</Table.Tbody>
</Table>
);
}
Table tabular numbers
Table component now supports tabularNums
prop to render numbers in tabular style. It sets
font-variant-numeric: tabular-nums
which makes numbers to have equal width.
This is useful when you have columns with numbers and you want them to be aligned:
import { NumberFormatter, Table } from '@mantine/core';
const data = [
{ product: 'Apples', unitsSold: 2214411234 },
{ product: 'Oranges', unitsSold: 9983812411 },
{ product: 'Bananas', unitsSold: 1234567890 },
{ product: 'Pineapples', unitsSold: 9948810000 },
{ product: 'Pears', unitsSold: 9933771111 },
];
function Demo() {
const rows = data.map((item) => (
<Table.Tr key={item.product}>
<Table.Td>{item.product}</Table.Td>
<Table.Td>
<NumberFormatter value={item.unitsSold} thousandSeparator />
</Table.Td>
</Table.Tr>
));
return (
<Table tabularNums>
<Table.Thead>
<Table.Tr>
<Table.Th>Product</Table.Th>
<Table.Th>Units sold</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
);
}
Update function in modals manager
Modals manager now supports modals.updateModal
and modals.updateContextModal
function to update modal after it was opened:
import { Button } from '@mantine/core';
import { modals } from '@mantine/modals';
function Demo() {
return (
<Button
onClick={() => {
const modalId = modals.open({
title: 'Initial Modal Title',
children: <Text>This text will update in 2 seconds.</Text>,
});
setTimeout(() => {
modals.updateModal({
modalId,
title: 'Updated Modal Title',
children: (
<Text size="sm" c="dimmed">
This is the updated content of the modal.
</Text>
),
});
}, 2000);
}}
>
Open updating modal
</Button>
);
}
useForm submitting state
use-form hook now supports form.submitting
field
and form.setSubmitting
function to track form submission state.
form.submitting
field will be set to true
if function passed to
form.onSubmit
returns a promise. After the promise is resolved or rejected,
form.submitting
will be set to false
:
import { useState } from 'react';
import { Button, Group, Stack, Text, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
const asyncSubmit = (values: any) =>
new Promise((resolve) => setTimeout(() => resolve(values), 3000));
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: { name: 'John' },
});
const [completed, setCompleted] = useState(false);
const handleSubmit = async (values: typeof form.values) => {
await asyncSubmit(values);
setCompleted(true);
};
if (completed) {
return (
<Stack>
<Text>Form submitted!</Text>
<Button onClick={() => setCompleted(false)}>Reset to initial state</Button>
</Stack>
);
}
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<TextInput
withAsterisk
label="Name"
placeholder="Your name"
key={form.key('name')}
disabled={form.submitting}
{...form.getInputProps('name')}
/>
<Group justify="flex-end" mt="md">
<Button type="submit" loading={form.submitting}>
Submit
</Button>
</Group>
</form>
);
}
You can also manually set form.submitting
to true
or false
:
import { useForm } from '@mantine/form';
const form = useForm({ mode: 'uncontrolled' });
form.submitting; // -> false
form.setSubmitting(true);
form.submitting; // -> true
form.setSubmitting(false);
form.submitting; // -> false
useForm onSubmitPreventDefault option
use-form hook now supports onSubmitPreventDefault
option.
This option is useful if you want to integrate useForm
hook with server actions.
By default, event.preventDefault()
is called on the form onSubmit
handler.
If you want to change this behavior, you can pass onSubmitPreventDefault
option
to useForm
hook. It can have the following values:
always
(default) - always callevent.preventDefault()
never
- never callevent.preventDefault()
validation-failed
- callevent.preventDefault()
only if validation failed
import { useForm } from '@mantine/form';
const form = useForm({
mode: 'uncontrolled',
onSubmitPreventDefault: 'never',
});
Subtle RichTextEditor variant
RichTextEditor component now supports subtle
variant:
import Highlight from '@tiptap/extension-highlight';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { RichTextEditor } from '@mantine/tiptap';
const content = '<p>Subtle rich text editor variant</p>';
function Demo() {
const editor = useEditor({
extensions: [StarterKit, Underline, Highlight],
content,
});
return (
<RichTextEditor editor={editor} variant="subtle">
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
);
}
onExitTransitionEnd and onEnterTransitionEnd
Modal and Drawer components now support onExitTransitionEnd
and onEnterTransitionEnd
props,
which can be used to run code after exit/enter transition is finished. For example, this is useful when you want to clear
data after modal is closed:
import { useState } from 'react';
import { Button, Group, Modal } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function Demo() {
const [firstOpened, firstHandlers] = useDisclosure(false);
const [secondOpened, secondHandlers] = useDisclosure(false);
const [modalData, setModalData] = useState({
title: '',
message: '',
});
return (
<>
<Modal
opened={firstOpened}
onClose={() => {
firstHandlers.close();
setModalData({ title: '', message: '' });
}}
title={modalData.title}
>
{modalData.message}
</Modal>
<Modal
opened={secondOpened}
onClose={secondHandlers.close}
onExitTransitionEnd={() => setModalData({ title: '', message: '' })}
title={modalData.title}
>
{modalData.message}
</Modal>
<Group>
<Button
onClick={() => {
firstHandlers.open();
setModalData({ title: 'Edit your profile', message: 'Imagine a form here' });
}}
>
Clear data in onClose
</Button>
<Button
onClick={() => {
secondHandlers.open();
setModalData({ title: 'Edit your profile', message: 'Imagine a form here' });
}}
>
Clear data in onExitTransitionEnd
</Button>
</Group>
</>
);
}
Week numbers in DatePicker
DatePicker and other components based on Calendar component now support withWeekNumbers
prop to display week numbers:
import { DatePicker } from '@mantine/dates';
function Demo() {
return <DatePicker withWeekNumbers />;
}
New demo: BarChart with overlay
import { BarChart } from '@mantine/charts';
import { data } from './data';
import classes from './Demo.module.css';
function Demo() {
const bigBarWidth = useMediaQuery('(min-width: 48em)') ? 42 : 26;
const ratio = 0.5;
const smallBarWidth = bigBarWidth * ratio;
const barGap = (bigBarWidth + smallBarWidth) / -2;
return (
<BarChart
h={300}
data={overlayData}
dataKey="index"
barChartProps={{ barGap }}
barProps={(data) => ({ barSize: data.name === 'you' ? bigBarWidth : smallBarWidth })}
classNames={classes}
series={[
{ name: 'you', color: 'var(--you-bar-color)' },
{ name: 'average', color: 'var(--average-bar-color)' },
]}
/>
);
}
Variants types augmentation
Custom variants types augmentation guide was added to the documentation.
Example of adding custom variant type to Button component:
import { ButtonVariant, MantineSize } from '@mantine/core';
type ExtendedButtonVariant = ButtonVariant | 'contrast' | 'radial-gradient';
declare module '@mantine/core' {
export interface ButtonProps {
variant?: ExtendedButtonVariant;
}
}
Help Center updates
- How to use Mantine template on GitHub? and How can I submit a template to Mantine documentation? pages were moved from the documentation to Help Center
- How that thing is done on mantine.dev website? question
- Why is it required to have 10 shades per color? question
- Why I see color scheme flickering on page load? question
- How can I test Modal/Drawer/Popover components? question