8.3.15
[@mantine/dropzone]Updatereact-dropzoneto 15.0.0 (#8667)[@mantine/core]TagsInput: Fix duplicate checking bypass with splitChars (#8686)[@mantine/charts]Allow ChartTooltipvalueFormatterto returnReact.ReactNode(#8650)[@mantine/dates]DatePicker: Fix placeholder selector missing in Styles API (#8663)[@mantine/core]Add missing factory types exports (#8677)[@mantine/core]Fix inert attribute being ignored by Checkbox and Switch components (#8668)
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.14...8.3.15
9.0.0-alpha.0
View changelog with demos on alpha.mantine.dev website
This is the first alpha version of Mantine 9. You are welcome to review changes and provide feedback.
You can now sponsor Mantine development with OpenCollective. All funds are used to improve Mantine and create new features and components.
This changelog covers breaking changes and new features in Mantine 9.0. To migrate your application to Mantine 9.0, follow 8.x → 9.x migration guide.
Starting from Mantine 9.0, the following dependencies are required:
- React 19+ for all
@mantine/*packages - Tiptap 3+ for
@mantine/tiptap(migration guide) - Recharts 3+ for
@mantine/charts(no migration required)
All Mantine components and hooks now provide namespace exports for related types. For example, use-disclosure hook types can now be accessed like this:
import { useDisclosure } from '@mantine/hooks';
const options: useDisclosure.Options = {
onOpen: () => console.log('open'),
onClose: () => console.log('close'),
};
function Demo() {
const [opened, handlers] = useDisclosure(options);
}
Example of using namespace types with Button props type:
import { Button } from '@mantine/core';
const buttonProps: Button.Props = {
variant: 'filled',
size: 'md',
disabled: false,
};
function Demo() {
return <Button {...buttonProps}>Click me</Button>;
}
New @mantine/schedule package provides a complete set of calendar scheduling components for React applications. It includes multiple view levels, drag-and-drop event management, and extensive customization options.
Components included:
- Schedule – unified container component that combines all views with built-in navigation and view switching
- DayView – single day view with configurable time slots, all-day event section, current time indicator, and business hours highlighting
- WeekView – weekly calendar grid with time slots, week numbers, weekend day toggling, and multi-day event spanning
- MonthView – monthly calendar grid with event overflow handling, outside days display, and week numbers
- YearView – 12-month year overview organized by quarters with day-level event indicators
- MobileMonthView – mobile-optimized month view with event details panel for the selected day
Key features:
- Drag and drop – drag events to reschedule them across time slots and days. Supports conditional dragging with
canDragEventcallback, and drag-to-select time ranges withwithDragSlotSelect - Multiple interaction modes –
defaultmode enables all interactions (click, drag, navigation),staticmode renders a read-only calendar - Responsive layout –
layout="responsive"automatically switches toYearViewandMobileMonthViewon smaller screens - Custom event rendering – use
renderEventBodyorrenderEventto fully customize how events are displayed in all views - Time configuration – configurable start/end times, time slot intervals, time label formats, and business hours highlighting
- Internationalization – full label customization through the
labelsprop, locale-aware date formatting via dayjs - Click handlers –
onTimeSlotClick,onAllDaySlotClick,onDayClick, andonEventClickfor building interactive scheduling interfaces - Styles API – full support for
classNames,styles, andunstyledprops, along with CSS variables for theming
To get started, follow the getting started guide.
Collapse component now supports horizontal orientation:
import { Button, Collapse, Stack, Typography } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function Demo() {
const [expanded, handlers] = useDisclosure(false);
return (
<Stack h={240} align="flex-start">
<Button onClick={handlers.toggle} w="fit-content">
{expanded ? 'Collapse' : 'Expand'}
</Button>
<Collapse expanded={expanded} orientation="horizontal">
<Typography bg="var(--mantine-color-blue-light)" p="xs" bdrs="md" w={200}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
</Typography>
</Collapse>
</Stack>
);
}
New use-collapse hook is the hook version of Collapse component. It allows animation of height from 0 to auto and vice versa.
import { Button, Typography } from '@mantine/core';
import { useCollapse, useDisclosure } from '@mantine/hooks';
function Demo() {
const [expanded, handlers] = useDisclosure(false);
const getCollapseProps = useCollapse({ expanded });
return (
<>
<Button onClick={handlers.toggle} mb="md">
{expanded ? 'Collapse' : 'Expand'}
</Button>
<div {...getCollapseProps()}>
<Typography bg="var(--mantine-color-blue-light)" p="xs" bdrs="md">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</Typography>
</div>
</>
);
}
use-horizontal-collapse works the same way as use-collapse but animates width instead of height:
import { Button, Stack, Typography } from '@mantine/core';
import { useDisclosure, useHorizontalCollapse } from '@mantine/hooks';
function Demo() {
const [expanded, handlers] = useDisclosure(false);
const { getCollapseProps } = useHorizontalCollapse({ expanded });
return (
<Stack h={240}>
<Button onClick={handlers.toggle} w="fit-content">
{expanded ? 'Collapse' : 'Expand'}
</Button>
<div {...getCollapseProps()}>
<Typography bg="var(--mantine-color-blue-light)" p="xs" bdrs="md" w={200}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
</Typography>
</div>
</Stack>
);
}
New use-floating-window hook allows creating floating draggable elements:
import { Button, CloseButton, Group, Paper, Portal, Text } from '@mantine/core';
import { useDisclosure, useFloatingWindow } from '@mantine/hooks';
function Demo() {
const [visible, handlers] = useDisclosure();
const floatingWindow = useFloatingWindow({
constrainToViewport: true,
constrainOffset: 20,
excludeDragHandleSelector: 'button',
initialPosition: { top: 300, left: 20 },
});
return (
<>
<Button onClick={handlers.toggle} variant="default">
{visible ? 'Hide' : 'Show'} floating window
</Button>
{visible && (
<Portal>
<Paper
w={280}
p="md"
withBorder
radius="md"
pos="fixed"
style={{ cursor: 'move', transition: 'box-shadow 70ms ease', zIndex: 400 }}
shadow={floatingWindow.isDragging ? 'md' : undefined}
ref={floatingWindow.ref}
>
<Group justify="space-between" mb="md">
<Text>Usage demo</Text>
<CloseButton onClick={handlers.close} />
</Group>
<Text fz="sm">This is a floating window. You can drag it around.</Text>
</Paper>
</Portal>
)}
</>
);
}
FloatingWindow provides component API for use-floating-window hook:
import { Button, CloseButton, FloatingWindow, Group, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function Demo() {
const [visible, handlers] = useDisclosure();
return (
<>
<Button onClick={handlers.toggle} variant="default">
{visible ? 'Hide' : 'Show'} floating window
</Button>
{visible && (
<FloatingWindow
w={280}
p="md"
withBorder
radius="md"
excludeDragHandleSelector="button"
initialPosition={{ top: 300, left: 20 }}
style={{ cursor: 'move' }}
>
<Group justify="space-between" mb="md">
<Text>Usage demo</Text>
<CloseButton onClick={handlers.close} />
</Group>
<Text fz="sm">This is a floating window. You can drag it around.</Text>
</FloatingWindow>
)}
</>
);
}
New OverflowList component displays list of items and collapses the overflowing items into a single element:
// OverflowListDemo.tsx
import { Badge, OverflowList } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<div style={{ resize: 'horizontal', overflow: 'auto', maxWidth: '100%' }}>
<OverflowList
data={data}
gap={4}
renderOverflow={(items) => <Badge>+{items.length} more</Badge>}
renderItem={(item, index) => <Badge key={index}>{item}</Badge>}
/>
</div>
);
}
// data.ts
export const data = [
'Apple',
'Banana',
'Cherry',
'Date',
'Elderberry',
'Fig',
'Grape',
'Honeydew',
'Indian Fig',
'Jackfruit',
'Kiwi',
'Lemon',
'Mango',
'Nectarine',
'Orange',
'Papaya',
];
Card component now supports horizontal orientation:
import { Box, Card, Group, RingProgress, Text } from '@mantine/core';
const completed = 1887;
const total = 2334;
const stats = [
{ value: 447, label: 'Remaining' },
{ value: 76, label: 'In progress' },
];
function Demo() {
const items = stats.map((stat) => (
<div key={stat.label}>
<Text>{stat.value}</Text>
<Text size="xs" c="dimmed">
{stat.label}
</Text>
</div>
));
return (
<Card padding="sm" radius="md" withBorder orientation="horizontal">
<Card.Section inheritPadding px="xs" withBorder>
<RingProgress
roundCaps
thickness={6}
size={150}
sections={[{ value: (completed / total) * 100, color: 'blue' }]}
label={
<div>
<Text ta="center" fz="lg">
{((completed / total) * 100).toFixed(0)}%
</Text>
<Text ta="center" fz="xs" c="dimmed">
Completed
</Text>
</div>
}
/>
</Card.Section>
<Card.Section inheritPadding px="md">
<Text fz="xl">Project tasks</Text>
<Box mt="xs">
<Text>1887</Text>
<Text fz="xs" c="dimmed">
Completed
</Text>
</Box>
<Group mt="sm">{items}</Group>
</Card.Section>
</Card>
);
}
Checkbox.Group and Switch.Group now support maxSelectedValues prop to limit the number of selected values. When the limit is reached, the remaining controls are disabled and cannot be selected.
import { Checkbox, Group } from '@mantine/core';
function Demo() {
return (
<Checkbox.Group defaultValue={['react']} maxSelectedValues={2}>
<Group>
<Checkbox value="react" label="React" />
<Checkbox value="svelte" label="Svelte" />
<Checkbox value="ng" label="Angular" />
<Checkbox value="vue" label="Vue" />
</Group>
</Checkbox.Group>
);
}
All Mantine input components based on Input component now support loading prop.
Set loading prop to display a loading indicator. By default, the loader is displayed on the right side of the input. You can change the position with the loadingPosition prop to 'left' or 'right'. This is useful for async operations like API calls, searches, or validations:
import { TextInput } from '@mantine/core';
function Demo() {
return <TextInput placeholder="Your email" loading />;
}
MultiSelect and TagsInput components now support renderPill prop to customize pill rendering:
import { MultiSelect, Pill, Avatar } from '@mantine/core';
const users = [
{ value: 'Emily Johnson', label: 'Emily Johnson', image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-7.png' },
{ value: 'Ava Rodriguez', label: 'Ava Rodriguez', image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-8.png' },
{ value: 'Olivia Chen', label: 'Olivia Chen', image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-4.png' },
{ value: 'Ethan Barnes', label: 'Ethan Barnes', image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-1.png' },
{ value: 'Mason Taylor', label: 'Mason Taylor', image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-2.png' },
];
function Demo() {
return (
<MultiSelect
data={users}
label="Candidates"
placeholder="Select candidates"
defaultValue={['Emily Johnson', 'Ava Rodriguez']}
renderPill={({ option, onRemove }) => {
const user = users.find((u) => u.value === option?.value);
return (
<Pill withRemoveButton onRemove={onRemove}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Avatar src={user?.image} size={16} />
{option?.label}
</div>
</Pill>
);
}}
/>
);
}
All clearable input components now support clearSectionMode prop that determines how the clear button and rightSection are rendered:
'both'(default) – render both the clear button andrightSection'rightSection'– render only the user-suppliedrightSection, ignore clear button'clear'– render only the clear button, ignorerightSection
This prop is supported by Select, Autocomplete, MultiSelect, TagsInput, FileInput, DateInput, DatePickerInput, MonthPickerInput, YearPickerInput, TimePicker, and DateTimePicker.
import { IconChevronDown } from '@tabler/icons-react';
import { Stack } from '@mantine/core';
import { DatePickerInput } from '@mantine/dates';
function Demo() {
return (
<Stack>
<DatePickerInput
label="clearSectionMode='both' (default)"
placeholder="Pick date"
defaultValue={new Date()}
clearable
rightSection={<IconChevronDown size={16} />}
clearSectionMode="both"
/>
<DatePickerInput
label="clearSectionMode='rightSection'"
placeholder="Pick date"
defaultValue={new Date()}
clearable
rightSection={<IconChevronDown size={16} />}
clearSectionMode="rightSection"
/>
<DatePickerInput
label="clearSectionMode='clear'"
placeholder="Pick date"
defaultValue={new Date()}
clearable
rightSection={<IconChevronDown size={16} />}
clearSectionMode="clear"
/>
</Stack>
);
}
New Marquee component creates continuous scrolling animation for content:
import { Marquee } from '@mantine/core';
import { MantineLogo } from '@mantinex/mantine-logo';
function Demo() {
return (
<Marquee gap="lg">
<MantineLogo width={80} type="full" color="blue" />
<MantineLogo width={80} type="full" color="cyan" />
<MantineLogo width={80} type="full" color="teal" />
<MantineLogo width={80} type="full" color="green" />
<MantineLogo width={80} type="full" color="lime" />
<MantineLogo width={80} type="full" color="yellow" />
<MantineLogo width={80} type="full" color="orange" />
<MantineLogo width={80} type="full" color="red" />
</Marquee>
);
}
New Scroller component displays horizontally scrollable content with navigation controls. It supports native scrolling via trackpad, shift + mouse wheel, touch gestures, and mouse drag:
import { Badge, Group, Scroller } from '@mantine/core';
function Demo() {
return (
<Scroller>
<Group gap="xs" wrap="nowrap">
{Array.from({ length: 20 }).map((_, index) => (
<Badge key={index} variant="light" size="lg">
Badge {index + 1}
</Badge>
))}
</Group>
</Scroller>
);
}
New use-scroller hook provides logic for creating custom scrollable containers with navigation controls:
import { Box, Button, Group } from '@mantine/core';
import { useScroller } from '@mantine/hooks';
function Demo() {
const scroller = useScroller();
return (
<Box>
<Group mb="md">
<Button
onClick={scroller.scrollStart}
disabled={!scroller.canScrollStart}
variant="default"
size="xs"
>
← Scroll left
</Button>
<Button
onClick={scroller.scrollEnd}
disabled={!scroller.canScrollEnd}
variant="default"
size="xs"
>
Scroll right →
</Button>
</Group>
<div
ref={scroller.ref}
{...scroller.dragHandlers}
style={{
overflow: 'auto',
cursor: scroller.isDragging ? 'grabbing' : 'grab',
}}
>
<Group wrap="nowrap" gap="md">
{Array.from({ length: 20 }).map((_, index) => (
<Box
key={index}
style={{
minWidth: 100,
height: 80,
backgroundColor: 'var(--mantine-color-blue-filled)',
borderRadius: 'var(--mantine-radius-md)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontWeight: 500,
}}
>
{index + 1}
</Box>
))}
</Group>
</div>
</Box>
);
}
use-form now supports passing second type argument TransformedValues to define the type of transformed values returned by form.getTransformedValues and form.onSubmit:
import { useForm } from '@mantine/form';
interface FormValues {
name: string;
locationId: string;
}
interface TransformedValues {
name: string;
locationId: number;
}
function Demo() {
const form = useForm<FormValues, TransformedValues>({
mode: 'uncontrolled',
initialValues: {
name: '',
locationId: '2',
},
transformValues: (values) => ({
...values,
locationId: Number(values.locationId),
}),
});
}
use-form validation rules can now be async – return a Promise that resolves to an error message or null. When any rule is async, form.validate(), form.validateField() and form.isValid() all return promises. The form.validating property is true while any async validation is in progress, and form.isValidating(path) checks individual fields.
Each rule receives an AbortSignal as the fourth argument. The signal is aborted when a newer validation supersedes the current one, which you can use to cancel in-flight HTTP requests.
import { Button, Group, Loader, TextInput } from '@mantine/core';
import { isEmail, useForm } from '@mantine/form';
// Simulates an async API call to check if the username is available
function checkUsernameAvailability(username: string, signal?: AbortSignal): Promise<string | null> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
const taken = ['admin', 'user', 'test', 'mantine'];
resolve(taken.includes(username.toLowerCase()) ? 'Username is already taken' : null);
}, 800);
signal?.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
});
}
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: { username: '', email: '' },
validate: {
username: async (value, _values, _path, signal) => {
if (value.trim().length < 3) {
return 'Username must be at least 3 characters';
}
return checkUsernameAvailability(value, signal);
},
email: isEmail('Invalid email'),
},
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<TextInput
withAsterisk
label="Username"
placeholder="Pick a username"
key={form.key('username')}
disabled={form.submitting}
rightSection={form.validating ? <Loader size={16} /> : null}
{...form.getInputProps('username')}
/>
<TextInput
withAsterisk
mt="md"
label="Email"
placeholder="your@email.com"
key={form.key('email')}
disabled={form.submitting}
{...form.getInputProps('email')}
/>
<Group justify="flex-end" mt="md">
<Button type="submit" loading={form.submitting}>
Submit
</Button>
</Group>
</form>
);
}
When using async validation with validateInputOnChange, set validateDebounce to avoid firing an API call on every keystroke:
import { Button, Group, Loader, TextInput } from '@mantine/core';
import { useForm, isEmail } from '@mantine/form';
// Simulates an async API call to check if the username is available
function checkUsernameAvailability(username: string, signal?: AbortSignal): Promise<string | null> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
const taken = ['admin', 'user', 'test', 'mantine'];
resolve(taken.includes(username.toLowerCase()) ? 'Username is already taken' : null);
}, 800);
signal?.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
});
}
function Demo() {
const form = useForm({
mode: 'uncontrolled',
initialValues: { username: '', email: '' },
// Debounce async validation by 500ms – prevents firing
// an API call on every keystroke
validateDebounce: 500,
validateInputOnChange: ['username'],
validate: {
username: async (value, _values, _path, signal) => {
if (value.trim().length < 3) {
return 'Username must be at least 3 characters';
}
return checkUsernameAvailability(value, signal);
},
email: isEmail('Invalid email'),
},
});
return (
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<TextInput
withAsterisk
label="Username"
description="Try: admin, user, test, mantine"
placeholder="Pick a username"
key={form.key('username')}
disabled={form.submitting}
rightSection={form.isValidating('username') ? <Loader size={16} /> : null}
{...form.getInputProps('username')}
/>
<TextInput
withAsterisk
mt="md"
label="Email"
placeholder="your@email.com"
key={form.key('email')}
disabled={form.submitting}
{...form.getInputProps('email')}
/>
<Group justify="flex-end" mt="md">
<Button type="submit" loading={form.submitting}>
Submit
</Button>
</Group>
</form>
);
}
SegmentedControl, Select, MultiSelect, Chip.Group, Switch.Group, Checkbox.Group and Radio.Group now support generic value type. You can pass primitive values (numbers, strings, boolean, null) as the type argument. The generic type is used for value, defaultValue, onChange and other props.
For example, generic type can now be used with SegmentedControl to specify string union:
import { SegmentedControl } from '@mantine/core';
function Demo() {
return (
<SegmentedControl<'orange' | 'grape' | 'apple'>
data={[
{ value: 'orange', label: 'Orange' },
{ value: 'grape', label: 'Grape' },
{ value: 'apple', label: 'Apple' },
]}
/>
);
}
Combobox component now supports virtualization. Virtualization support is currently in alpha stage and the API may change in future releases. You are welcome to try it out and provide feedback.
Example of implementation with useVirtualizedCombobox and react-virtuoso:
import { useRef, useState } from 'react';
import { ListRange, Virtuoso } from 'react-virtuoso';
import { Combobox, Input, InputBase, ScrollArea, useVirtualizedCombobox } from '@mantine/core';
const largeData = Array(10000)
.fill(0)
.map((_, index) => ({
value: `value-${index}`,
label: `Label ${index}`,
id: `item-${index}`,
disabled: false,
}));
function Demo() {
const [opened, setOpened] = useState(false);
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
const [activeOptionIndex, setActiveOptionIndex] = useState(-1);
const [value, setValue] = useState('');
const virtuoso = useRef<any>(null);
const viewportRef = useRef<HTMLDivElement>(null);
const visibleRangeRef = useRef<ListRange>({
startIndex: 0,
endIndex: 0,
});
const scrollOptionIntoView = (index: number) => {
setTimeout(() => {
const isVisible =
index >= visibleRangeRef.current.startIndex && index <= visibleRangeRef.current.endIndex;
if (index !== -1 && !isVisible) {
virtuoso.current.scrollToIndex({ index, align: 'end' });
}
}, 4);
};
const combobox = useVirtualizedCombobox({
opened,
onOpenedChange: setOpened,
onDropdownOpen: () => {
if (activeOptionIndex !== -1) {
combobox.selectActiveOption();
scrollOptionIntoView(activeOptionIndex);
}
},
isOptionDisabled: (index) => largeData[index].disabled,
totalOptionsCount: largeData.length,
getOptionId: (index) => largeData[index].id,
selectedOptionIndex,
activeOptionIndex,
setSelectedOptionIndex: (index) => {
setSelectedOptionIndex(index);
scrollOptionIntoView(index);
},
onSelectedOptionSubmit: onOptionSubmit,
});
function onOptionSubmit(index: number) {
const option = largeData[index];
setValue(option.value);
setActiveOptionIndex(index);
combobox.closeDropdown();
combobox.resetSelectedOption();
}
return (
<Combobox
store={combobox}
resetSelectionOnOptionHover={false}
keepMounted
onOptionSubmit={(option) => {
setValue(option);
combobox.openDropdown();
}}
>
<Combobox.Target>
<InputBase component="button" onClick={() => combobox.toggleDropdown()} pointer>
{value || <Input.Placeholder>Pick a value</Input.Placeholder>}
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options>
<ScrollArea.Autosize
mah={220}
type="scroll"
scrollbarSize={4}
viewportRef={viewportRef}
onMouseDown={(event) => event.preventDefault()}
>
<Virtuoso
data={largeData}
ref={virtuoso}
style={{ height: 420 }}
customScrollParent={viewportRef.current!}
rangeChanged={(range) => {
visibleRangeRef.current = range;
}}
itemContent={(index, item) => (
<Combobox.Option
value={item.value}
key={item.value}
selected={index === selectedOptionIndex}
onClick={() => onOptionSubmit(index)}
>
{item.label}
</Combobox.Option>
)}
/>
</ScrollArea.Autosize>
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
);
}
Highlight component now supports custom colors for individual highlight terms. You can provide an array of objects with text and color properties to assign different colors to different highlighted terms:
import { Highlight } from '@mantine/core';
function Demo() {
return (
<Highlight
highlight={[
{ text: 'error', color: 'red' },
{ text: 'warning', color: 'yellow' },
{ text: 'success', color: 'green' },
]}
>
Error: Invalid input. Warning: Check this field. Success: All tests passed.
</Highlight>
);
}
Highlight component now supports wholeWord prop to match only complete words. When enabled, 'the' will not match 'there' or 'theme':
import { Highlight, Stack, Text } from '@mantine/core';
function Demo() {
return (
<Stack gap="md">
<div>
<Text size="sm" fw={500} mb={5}>
With whole word matching (wholeWord={'{'}true{'}'})
</Text>
<Highlight highlight="the" wholeWord>
The theme is there
</Highlight>
</div>
<div>
<Text size="sm" fw={500} mb={5}>
Without whole word matching (default)
</Text>
<Highlight highlight="the">The theme is there</Highlight>
</div>
</Stack>
);
}
Pagination component and use-pagination hook now support startValue prop to define the starting page number. For example, with startValue={5} and total={15}, the pagination range will be from 5 to 15:
import { Text, Pagination } from '@mantine/core';
function Demo() {
return (
<>
<Text mb="xs">Pages 5–15 (startValue=5, total=15)</Text>
<Pagination total={15} startValue={5} defaultValue={5} />
</>
);
}
Grid component no longer uses negative margins for spacing between columns. Instead, it now uses native CSS gap property, which means you no longer need to use overflow="hidden" to prevent content overflow caused by negative margins.
Slider and RangeSlider components now support vertical orientation:
import { RangeSlider, Slider } from '@mantine/core';
const marks = [
{ value: 20, label: '20%' },
{ value: 50, label: '50%' },
{ value: 80, label: '80%' },
];
function Demo() {
return (
<div style={{ display: 'flex', gap: 40 }}>
<Slider orientation="vertical" defaultValue={45} marks={marks} />
<RangeSlider orientation="vertical" defaultValue={[25, 65]} marks={marks} />
</div>
);
}
SimpleGrid component now supports minColWidth prop to use CSS Grid auto-fill/auto-fit to automatically adjust the number of columns based on available space and minimum column width. When minColWidth is set, the cols prop is ignored. Use autoFlow prop to switch between auto-fill (default) and auto-fit behavior.
import { SimpleGrid } from '@mantine/core';
function Demo() {
return (
<SimpleGrid minColWidth="200px">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</SimpleGrid>
);
}
SimpleGrid also now supports autoRows prop to control the size of implicitly created grid rows:
import { SimpleGrid } from '@mantine/core';
function Demo() {
return (
<SimpleGrid cols={3} autoRows="minmax(100px, auto)">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</SimpleGrid>
);
}
New BarsList component displays a list of horizontal bars with names and values. It supports custom colors, auto contrast, value formatting, and custom bar rendering:
// Demo.tsx
import { BarsList } from '@mantine/charts';
import { data } from './data';
function Demo() {
return <BarsList data={data} />;
}
// data.ts
export const data = ${JSON.stringify(data, null, 2)};
- New Custom components guide explaining how to create custom components with Mantine's styling system
- New Controlled vs Uncontrolled guide explaining differences between controlled and uncontrolled components
- HueSlider and AlphaSlider components now have their own documentation pages
- Uncontrolled documentation and usage with
FormDatasection was added to all inputs components - JsonInput documentation now includes custom serialization example with
superjsonlibrary - Pagination documentation now includes URL synchronization examples for Next.js, react-router-dom and nuqs
- use-form documentation now includes separate examples with all Mantine inputs
lightvariant in all components now uses different colors values without transparency to improve contrastmodprop now converts camelCase keys to kebab-case for data attributes in all components@mantine/formpackage no longer exports schema resolvers, use dedicated packages instead@mantine/formvalidation is now natively async –form.validate(),form.validateField()andform.isValid()return promises. Newform.validating,form.isValidating(path),validateDebounceandresolveValidationErroroptions were added.createPolymorphicComponentfunction was renamed to shorterpolymorphicfor convenience- All Mantine components now use
font-weight: 600instead offont-weight: 500for better readability - All Mantine components now support logical margin and padding style props:
mis- margin-inline-startmie- margin-inline-endpis- padding-inline-startpie- padding-inline-end
- Tree component now supports controlled state via
expandedState,selectedStateandcheckedStateprops. - Tree component no longer defines
data-hoveredattribute for hover state, you need to apply hover styles with&:hoverinstead. This change improves rendering performance by resolving this issue. - Collapse component now uses
expandedprop instead ofin - Collapse, NavLink and Accordion.Panel now support
keepMounted={false}prop to unmount collapsed content - Select and MultiSelect components now support primitive value types (numbers, booleans, strings) for data and value
- MultiSelect now supports
onMaxValuesprop, which is called when the user attempts to select more values thanmaxValues - TagsInput component now supports
onMaxTagsprop, which is called when the user attempts to add more tags thanmaxTags - Accordion component now supports
refprop - Text and Anchor components no longer accept
colorprop, usecstyle prop instead - PasswordInput component visibility toggle icon was updated
- Popover and Tooltip components no longer accept
positionDependenciesprop, it is no longer required - TypographyStylesProvider component was renamed to Typography
- Checkbox component now supports
readOnlyandwithErrorStylesprops - Spoiler component:
initialStateprop was renamed todefaultExpandedfor consistency with other components- New
showAriaLabelandhideAriaLabelprops allow customizing ARIA labels
- Checkbox.Group and Switch.Group can now be used in uncontrolled forms and can be accessed through
FormData - ColorPicker component now supports
nameandhiddenInputPropsprops to include color value in uncontrolled form submissions - Dialog now enables
withBorderby default - Pagination component now supports
input-prefix forsizeprop to match input and button sizes - FloatingIndicator component now supports
onTransitionStartandonTransitionEndcallbacks - LoadingOverlay component now supports
onEnter,onEntered,onExitandonExitedcallbacks - Grid component
gutterprop was renamed togapfor consistency with other layout components. NewrowGapandcolumnGapprops allow separate control of row and column spacing. Grid.Col now supportsalignprop for per-column vertical alignment. - Indicator component now supports:
maxValueprop to display{maxValue}+when the label exceeds the maximum valueshowZeroprop (defaulttrue) to control visibility of indicator with label0offsetprop object withxandyproperties for separate horizontal and vertical offsets
- NumberInput component now supports:
onMinReachedandonMaxReachedcallbacksselectAllOnFocusprop to select all text when input is focused
- RingProgress component now supports
sectionGapprop to add visual separation between sections in degreesstartAngleprop to control where the progress starts (0 = right, 90 = bottom, 180 = left, 270 = top)
- List component now supports HTML5 list attributes:
start,reversed, andvalueprops for enhanced semantic HTML support - JsonInput component now supports
indentSpacesprop to control the number of spaces used for formatting JSON - Rating component now supports
allowClearprop to reset rating to 0 by clicking the same value - ScrollArea component now supports
onLeftReachedandonRightReachedcallbacks for horizontal scroll boundaries - Slider and RangeSlider now support hidden marks with
hidden: trueproperty. Hidden marks allow snapping to specific values without displaying them visually, useful withrestrictToMarksprop. - use-tree no longer supports callback state setters for
setExpandedState,setSelectedState, andsetCheckedStatefunctions - use-fullscreen hook was split into two hooks:
useFullscreenElementanduseFullscreenDocument - use-media-query hook no longer includes fallback for old Safari versions (iOS 13 and earlier, released before 2019)
- use-resize-observer now uses the new callback ref approach. The new approach makes hook usable with dynamic node changes. This change might be breaking, validate hook usage in your application.
- use-mouse hook now uses the new callback ref approach to resolve the issue with stale refs. The previous hook functionality was split into two hooks:
use-mouse(for ref) anduse-mouse-position(for document). - use-mutation-observer hook now uses the new callback ref approach. The new approach makes hook usable with dynamic node changes. Additionally, a new
useMutationObserverTargethook was added for observing external target elements. - use-disclosure hook now supports new
sethandler - use-floating-indicator hook now supports
onTransitionStartandonTransitionEndcallbacks @mantine/hookstypes were renamed for consistency:UseScrollSpyReturnType→UseScrollSpyReturnValueStateHistory→UseStateHistoryValueOS→UseOSReturnValue
8.3.14
[@mantine/core]Switch: Fix checkbox not being recognized by Playwright (#8370, #8645)[@mantine/core]MultiSelect: Fix click on chevron not opening dropdown when clearable is enabled (#8641)[@mantine/modals]Fix types of context modals inferred incorrectly (#8625)[@mantine/core]MultiSelect: Fix clear button overlapping with pills (#8634)
- @ckhe1215 made their first contribution in https://github.com/mantinedev/mantine/pull/8634
- @RaphalRiad made their first contribution in https://github.com/mantinedev/mantine/pull/8625
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.13...8.3.14
8.3.13
[@mantine/core]AddopenOnFocusprop to Combobox based components (#5893, #8623)[@mantine/dates]TimePicker: Fix clearing in uncontrolled mode not updating to empty value (#8622)[@mantine/core]ScrollArea: Fix Shift + wheel scroll not working correctly on Windows (#8619)[@mantine/core]AddselectFirstOptionOnDropdownOpento Combobox based components (#8597)[@mantine/core]ScrollArea: FixonBottomReachednot being called when used in zoomed-in viewports (#8616)[@mantine/core]Popover: Fixaria-controlsattribute being set on the target element when the dropdown is not mounted (#8595)[@mantine/core]RangeSlider: Fix incorrect Styles API name (#8601)[@mantine/core]ScrollArea: FixoverscrollBehaviorprop not working in ScrollArea.Autosize component (#8611)
- @AgentRBY made their first contribution in https://github.com/mantinedev/mantine/pull/8601
- @jonathanhuynh70 made their first contribution in https://github.com/mantinedev/mantine/pull/8595
- @robert-j-webb made their first contribution in https://github.com/mantinedev/mantine/pull/8616
- @Alan-Gomes made their first contribution in https://github.com/mantinedev/mantine/pull/8597
- @pggggggggh made their first contribution in https://github.com/mantinedev/mantine/pull/8622
- @harry-ignite made their first contribution in https://github.com/mantinedev/mantine/pull/8623
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.12...8.3.13
8.3.12
[@mantine/core]Popover: Fix flip logic being calculated after shift (#8590)[@mantine/tiptap]Fix bubble menu being overlapped by the toolbar (#8589)[@mantine/core]ScrollArea: Fix incorrect overscroll behavior whenscrollbarexplicitly set on one side (#8591)[@mantine/core]AppShell: Add static mode support for nested app shells
- @Hosung99 made their first contribution in https://github.com/mantinedev/mantine/pull/8591
- @kykim00 made their first contribution in https://github.com/mantinedev/mantine/pull/8590
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.11...8.3.12
8.3.11
[@mantine/core]Combobox: Fix dropdown not being positioned correctly when element is initially rendered outside the viewport (#8520, #8577)[@mantine/core]NumberInput: Fix decimal separator being removed with Backspace key (#8571)[@mantine/code-highlight]AddCodeHighlightAdaptertype export (#8581)[@mantine/form]AddLooseKeystype export (#8543)[@mantine/core]FileInput: Fix incorrect default size[@mantine/hooks]use-focus-within: Fix stale closure inonFocus/onBlurcallbacks (#8528)[@mantine/dates]TimePicker: Fix AM/PM input displaying arbitrary text (#8532)[@mantine/core]Notification: Fix incorrectvariantprop type (#8537)[@mantine/carousel]Update controls DOM order to have proper tab order
- @jakub-gaik made their first contribution in https://github.com/mantinedev/mantine/pull/8537
- @muhammedaksam made their first contribution in https://github.com/mantinedev/mantine/pull/8532
- @ReemX made their first contribution in https://github.com/mantinedev/mantine/pull/8543
- @iamdevlinph made their first contribution in https://github.com/mantinedev/mantine/pull/8573
- @akash191095 made their first contribution in https://github.com/mantinedev/mantine/pull/8568
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.10...8.3.11
8.3.10
[@mantine/hooks]use-focus-within: Fix event handlers not being cleaned up properly (#8507)[@mantine/core]Menu: Fix focus not being returned to the target element when the dropdown is closed (#8517)[@mantine/core]List: Fix incorrect marker gap withiconprop (#8519)[@mantine/core]Fix some global styles not being applied to shadow DOM:host(#8504)[@mantine/core]Fix SSR-related errors in Tooltip and Popover.Target when the children are defined withReact.lazy(#8522)[@mantine/tiptap]Fix missing z-index on the toolbar[@mantine/form]AddFormArrayElementtype export (#8486)[@mantine/dates]Fix children override not working in MonthsList/YearsListgetYearProps/getMonthPropsfunctions (#8488)[@mantine/emotion]ImproveCSSObjecttype increateStyles(#8500)[@mantine/core]Input: Fix description having incorrect cursor styles (#8503)[@mantine/hooks]use-in-viewport: Fix incorrect entry cheching in some cases (#8509)[@mantine/core]Popover: Fix incorrect dropdown position when the target element is not initially within the viewport (#8411)[@mantine/core]Fix incorrectly inheritedpointerprop type from base input in some components
- @spalt08 made their first contribution in https://github.com/mantinedev/mantine/pull/8509
- @radui1398 made their first contribution in https://github.com/mantinedev/mantine/pull/8503
- @MehdiAlipooor made their first contribution in https://github.com/mantinedev/mantine/pull/8488
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.9...8.3.10
8.3.9
[@mantine/core]ColorInput: Fix incorrect action icon size (#8481)[@mantine/form]FixinsertListItemandreplaceListItemhandlers not being type safe (#8478)[@mantine/dates]DatePickerInput: FixplaceholderStyles API selector not working (#8405)[@mantine/core]ColorInput: Fix incorrect font-size ifsizeprop not set explicitly (#8472)[@mantine/emotion]AddmergeSxutility function (#8471)[@mantine/core]ActionIcon: Fix incorrect disabled state background-color for gradient variant[@mantine/modals]Fix breaking changes accidentally introduced in the previous patch (#8476)[@mantine/core]Combobox: AddwithAlignedLabelsprop support to offset selected check icon in the dropdown options (#8391)[@mantine/core]Badge: Fixcircleprop not working whenradiusprop is set explicitly (#8466)[@mantine/core]Checkbox: Fix incorrectindeterminatestate calculation (#8460)[@mantine/core]Radio: Fix Radio.Group switching from uncontrolled to controlled mode when value is explicitly set (#8453)
- @semimikoh made their first contribution in https://github.com/mantinedev/mantine/pull/8466
- @oguzgelal made their first contribution in https://github.com/mantinedev/mantine/pull/8471
- @Copilot made their first contribution in https://github.com/mantinedev/mantine/pull/8478
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.8...8.3.9
8.3.8
[@mantine/modals]Improve types of context modals (#8439)[@mantine/core]Combobox: Fix keyboard interactions not working when used in shadow DOM (#8438)[@mantine/core]NumberInput: Fix,decimal separator not being converted to.when entering value from mobile keyboard[@mantine/core]NumberInput: Fix incorrect keyboard displayed on iOS 26[@mantine/charts]Add cellProps prop support to DonutChart and PieChart (#8444)[@mantine/core]Alert: Allow overridingroleattribute (#8447)[@mantine/core]Menu: AddopenDelayprop support to Menu.Sub component (#8437)
- @ScotchAndSoda made their first contribution in https://github.com/mantinedev/mantine/pull/8444
- @justinwhall made their first contribution in https://github.com/mantinedev/mantine/pull/8438
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.7...8.3.8
8.3.7
[@mantine/modals]AddConfirmModalPropstype exports (#8414)[@mantine/core]AppShell: Fix unexpected Header transition when scrollbar becomes hidden via scroll lock (#8420)[@mantine/core]Adddisabledprop for Radio.Group, Checkbox.Group and Switch.Group (#8396)[@mantine/core]Fix missing sub Menu components exports (#8419)[@mantine/tiptap]Fix editor control being overridden by customonClickhandler (#8421)[@mantine/core]PasswordInput: Addaria-pressedattribute to the visibility toggle (#8425)[@mantine/tiptap]Fix bubble menu being overlapped by the toolbar (#8416)[@mantine/core]Chip: RemoveiconWrapperif icon is exlicitly disabled[@mantine/charts]AddlabelFormattersupport to the default tooltip (#8404)
- @Visualizeit made their first contribution in https://github.com/mantinedev/mantine/pull/8419
- @Spichkenss made their first contribution in https://github.com/mantinedev/mantine/pull/8407
- @schummar made their first contribution in https://github.com/mantinedev/mantine/pull/8420
Full Changelog: https://github.com/mantinedev/mantine/compare/8.3.6...8.3.7