8.0.0
mantinedev/mantine8.0.0May 5, 2025by rtivital
AI Summary
Major version introducing granular global styles, submenus in Menu, migration of date values to strings, and a new TimePicker component.
Key Highlights
- Granular global styles exports (baseline, default-css-variables, global)
- Menu component now supports submenus
- Popover component gains hideDetached prop
- Date values migrated from Date objects to strings (YYYY-MM-DD)
- DatesProvider timezone option removed
- New TimePicker component replaces TimeInput in DateTimePicker
Breaking Changes
- Global styles imports changed: must import baseline.css, default-css-variables.css, and global.css individually
- Date values changed from Date objects to strings in @mantine/dates components
- DateTimePicker timeInputProps prop removed, replaced by timePickerProps
- DatesProvider timezone prop removed
New Features
- Menu Submenus
- Popover hideDetached
- TimePicker component
- TimeValue component
- Granular styles exports
Full Release Notes
[View changelog with demos on mantine.dev website](https://mantine.dev/changelog/8-0-0)
## Migration guide
This changelog covers breaking changes and new features in Mantine 8.0.
To migrate your application to Mantine 8.0, follow [7.x → 8.x migration guide](https://mantine.dev/guides/7x-to-8x).
## Granular global styles exports
Global styles are now splitted between 3 files:
- `baseline.css` – a minimal CSS reset, sets `box-sizing: border-box` and changes font properties
- `default-css-variables.css` – contains all CSS variables generated from the default theme
- `global.css` – global classes used in Mantine components
If you previously imported individual styles from `@mantine/core` package, you need to update imports
to use new files:
```tsx
import '@mantine/core/styles/baseline.css';
import '@mantine/core/styles/default-css-variables.css';
import '@mantine/core/styles/global.css';
```
If you imported `@mantine/core/styles.css`, no changes are required –
all new files are already included in `styles.css`.
## Menu with submenus
[Menu](https://mantine.dev/core/menu) component now supports submenus:
```tsx
import { Button, Menu } from '@mantine/core';
function Demo() {
return (
<Menu width={200} position="bottom-start">
<Menu.Target>
<Button>Toggle Menu</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>Dashboard</Menu.Item>
<Menu.Sub>
<Menu.Sub.Target>
<Menu.Sub.Item>Products</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>All products</Menu.Item>
<Menu.Item>Categories</Menu.Item>
<Menu.Item>Tags</Menu.Item>
<Menu.Item>Attributes</Menu.Item>
<Menu.Item>Shipping classes</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
<Menu.Sub>
<Menu.Sub.Target>
<Menu.Sub.Item>Orders</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>Open</Menu.Item>
<Menu.Item>Completed</Menu.Item>
<Menu.Item>Cancelled</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
<Menu.Sub>
<Menu.Sub.Target>
<Menu.Sub.Item>Settings</Menu.Sub.Item>
</Menu.Sub.Target>
<Menu.Sub.Dropdown>
<Menu.Item>Profile</Menu.Item>
<Menu.Item>Security</Menu.Item>
<Menu.Item>Notifications</Menu.Item>
</Menu.Sub.Dropdown>
</Menu.Sub>
</Menu.Dropdown>
</Menu>
);
}
```
## Popover hideDetached
[Popover](https://mantine.dev/core/popover) component now supports `hideDetached` prop to configure how the dropdown behaves when the target
element is hidden with styles (`display: none`, `visibility: hidden`, etc.),
removed from the DOM, or when the target element is scrolled out of the viewport.
By default, `hideDetached` is enabled – the dropdown is hidden with the target element.
You can change this behavior with `hideDetached={false}`. To see the difference, try to scroll
the root element of the following demo:
```tsx
import { Box, Button, Group, Popover } from '@mantine/core';
function Demo() {
return (
<Box
bd="1px solid var(--mantine-color-dimmed)"
p="xl"
w={400}
h={200}
style={{ overflow: 'auto' }}
>
<Box w={1000} h={400}>
<Group>
<Popover width="target" position="bottom" opened>
<Popover.Target>
<Button>Toggle popover</Button>
</Popover.Target>
<Popover.Dropdown>This popover dropdown is hidden when detached</Popover.Dropdown>
</Popover>
<Popover width="target" position="bottom" opened hideDetached={false}>
<Popover.Target>
<Button>Toggle popover</Button>
</Popover.Target>
<Popover.Dropdown>This popover dropdown is visible when detached</Popover.Dropdown>
</Popover>
</Group>
</Box>
</Box>
);
}
```
## Date values as strings
All `@mantine/dates` components now use date strings in `YYYY-MM-DD` or `YYYY-MM-DD HH:mm:ss`
format instead of `Date` objects. This change was made to resolve issues related to timezones
– now the output of all `@mantine/dates` components does not include any timezone-specific information.
Follow [7.x → 8.x migration guide](https://mantine.dev/guides/7x-to-8x) to learn how to update the code to use new string values.
Example of using [DatePicker](https://mantine.dev/dates/date-picker) component with string values:
```tsx
import { useState } from 'react';
import { DatePicker } from '@mantine/dates';
function Demo() {
const [value, setValue] = useState<string | null>(null);
return <DatePicker value={value} onChange={setValue} />;
}
```
## DatesProvider timezone
`DatesProvider` component no longer supports `timezone` option – all `@mantine/dates`
components now use strings in `YYYY-MM-DD HH:mm:ss` format as values and do not contain
timezone information.
If you need to handle timezones in your application, you can use a dedicated dates library
([dayjs](https://day.js.org/), [luxon](https://moment.github.io/luxon/#/), [date-fns](https://date-fns.org/))
to update timezone values.
Example of using Mantine components with [dayjs](https://day.js.org/):
```tsx
import dayjs from 'dayjs';
import { DatePicker } from '@mantine/dates';
function Demo() {
const [value, setValue] = useState<string | null>('2022-08-21');
// Mantine components use strings as values, you can pass these
// strings to a dates library of your choice to assign timezone
const dateWithTimeZone = dayjs(value).tz("America/Toronto").toDate();
return <DatePicker value={value} onChange={setValue} />;
}
```
## TimePicker component
New [TimePicker](https://mantine.dev/dates/time-picker) component is an alternative to
[TimeInput](https://mantine.dev/dates/time-input) that offers more features. It supports 24-hour and 12-hour formats,
dropdown with hours, minutes and seconds, and more.
```tsx
import { TimePicker } from '@mantine/dates';
function Demo() {
return (
<>
<TimePicker label="Enter time (24h format)" withSeconds withDropdown />
<TimePicker label="Enter time (12h format)" withSeconds withDropdown format="12h" mt="md" />
</>
);
}
```
## DateTimePicker component changes
[DateTimePicker](https://mantine.dev/dates/date-time-picker) component now uses [TimePicker](https://mantine.dev/dates/time-picker) under
the hood instead of [TimeInput](https://mantine.dev/dates/time-input). You can now use all [TimePicker](https://mantine.dev/dates/time-picker)
features with [DateTimePicker](https://mantine.dev/dates/date-time-picker) component.
Prop `timeInputProps` is no longer available, to pass props down to the underlying [TimePicker](https://mantine.dev/dates/time-picker)
you need to use `timePickerProps` prop.
Example of enabling dropdown and setting `12h` format for time picker:
```tsx
import { DateTimePicker } from '@mantine/dates';
function Demo() {
return (
<DateTimePicker
label="Pick date and time"
placeholder="Pick date and time"
timePickerProps={{
withDropdown: true,
popoverProps: { withinPortal: false },
format: '12h',
}}
/>
);
}
```
## TimeValue component
New [TimeValue](https://mantine.dev/dates/time-value) component can be used to display a formatted time string
with similar formatting options to [TimePicker](https://mantine.dev/dates/time-picker) component.
```tsx
import { Text } from '@mantine/core';
import { TimeValue } from '@mantine/dates';
function Demo() {
return (
<div>
<Text>
24h format: <TimeValue value="18:45:34" />
</Text>
<Text>
12h format: <TimeValue value="18:45:34" format="12h" />
</Text>
</div>
);
}
```
## TimeGrid component
New [TimeGrid](https://mantine.dev/dates/time-grid) component allows to capture time value from the user with a
predefined set of time slots:
```tsx
import { getTimeRange, TimeGrid } from '@mantine/dates';
function Demo() {
return (
<TimeGrid
data={getTimeRange({ startTime: '10:00', endTime: '21:00', interval: '01:00' })}
simpleGridProps={{
type: 'container',
cols: { base: 1, '180px': 2, '320px': 3 },
spacing: 'xs',
}}
withSeconds={false}
/>
);
}
```
## Heatmap component
New [Heatmap](https://mantine.dev/charts/heatmap) component allows to display data in a calendar heatmap format:
```tsx
import dayjs from 'dayjs';
import { Heatmap } from '@mantine/charts';
import { data } from './data';
function Demo() {
return (
<Heatmap
data={data}
startDate="2024-02-16"
endDate="2025-02-16"
withTooltip
withWeekdayLabels
withMonthLabels
getTooltipLabel={({ date, value }) =>
`${dayjs(date).format('DD MMM, YYYY')} – ${value === null || value === 0 ? 'No contributions' : `${value} contribution${value > 1 ? 's' : ''}`}`
}
/>
);
}
```
## CodeHighlight changes
[@mantine/code-highlight](https://mantine.dev/x/code-highlight) package no longer depends on
[highlight.js](https://highlightjs.org/). Instead, it now provides a new API based
on adapters that allows using any syntax highlighter of your choice. Out of the box,
`@mantine/code-highlight` provides adapters for [shiki](https://shiki.matsu.io/) and
[highlight.js](https://highlightjs.org/).
To learn about the migration process and how to use the new adapters API, check the
updated [CodeHighlight documentation](https://mantine.dev/x/code-highlight) and [7.x → 8.x migration guide](https://mantine.dev/guides/7x-to-8x).
## CodeHighlight with shiki
You can now use [CodeHighlight](https://mantine.dev/x/code-highlight) component with [shiki](https://shiki.matsu.io/).
[Shiki](https://shiki.matsu.io/) library provides the most advanced syntax highlighting
for TypeScript and CSS/Sass code. It uses textmate grammars to highlight code (same as in VSCode).
Shiki adapter is recommended if you need to highlight advanced TypeScript (generics, jsx nested in props) or CSS code (custom syntaxes, newest features).
Shiki adapter is used for all code highlighting in Mantine documentation.
To use shiki adapter, you need to install `shiki` package:
```sh
yarn add shiki
```
Then wrap your app with `CodeHighlightAdapterProvider` and provide `createShikiAdapter` as `adapter` prop:
```tsx
import { MantineProvider } from '@mantine/core';
import { CodeHighlightAdapterProvider, createShikiAdapter } from '@mantine/code-highlight';
// Shiki requires async code to load the highlighter
async function loadShiki() {
const { createHighlighter } = await import('shiki');
const shiki = await createHighlighter({
langs: ['tsx', 'scss', 'html', 'bash', 'json'],
themes: [],
});
return shiki;
}
const shikiAdapter = createShikiAdapter(loadShiki);
function App() {
return (
<MantineProvider>
<CodeHighlightAdapterProvider adapter={shikiAdapter}>
{/* Your app here */}
</CodeHighlightAdapterProvider>
</MantineProvider>
);
}
```
After that, you can use `CodeHighlight` component in your application:
```tsx
import { CodeHighlight } from '@mantine/code-highlight';
const exampleCode = `
type FilterPropsRes<T extends Record<string, any>> = {
[Key in keyof T]-?: T[Key] extends undefined ? never : T[Key];
};
export function filterProps<T extends Record<string, any>>(props: T) {
return Object.keys(props).reduce<FilterPropsRes<T>>((acc, key: keyof T) => {
if (props[key] !== undefined) {
acc[key] = props[key];
}
return acc;
}, {} as FilterPropsRes<T>);
}
`;
function Demo() {
return <CodeHighlight code={exampleCode} language="tsx" radius="md" />;
}
```
## Carousel changes
[@mantine/carousel](https://mantine.dev/x/carousel) package was updated to use the latest version of
`embla-carousel-react` package. This update includes breaking changes:
- `speed` and `draggable` props were removed – they are no longer supported by `embla-carousel-react`
- It is now required to install both `embla-carousel` and `embla-carousel-react` packages explicitly
- `useAnimationOffsetEffect` hook was removed – the issue it addressed was fixed in `embla-carousel-react`
- `Embla` type export was removed, you should use `EmblaCarouselType` from `embla-carousel` instead
- Props that were previously passed to embla are now grouped under `emblaOptions` prop
Follow the [7.x → 8.x migration guide](https://mantine.dev/guides/7x-to-8x) to update your application to use the latest version of `@mantine/carousel`.
## Switch withThumbIndicator
[Switch](https://mantine.dev/core/switch) component styles were updated to include indicator inside the thumb.
You can change it by setting `withThumbIndicator` prop:
```tsx
import { Switch } from '@mantine/core';
function Demo() {
return (
<Switch
defaultChecked
label="I agree to sell my privacy"
/>
);
}
```
## Theme types augmentation
You can now augment `spacing`, `radius`, `breakpoints`, `fontSizes`, `lineHeights`,
and `shadows` types. To learn more about this feature, follow [this guide](https://mantine.dev/guides/typescript/#theme-object-declarations).
Example of types augmentation for `spacing` and `radius`:
```tsx
import {
DefaultMantineSize,
MantineThemeSizesOverride,
} from '@mantine/core';
type ExtendedCustomSpacing =
| 'xxl'
| 'xxxs'
| DefaultMantineSize;
type ExtendedCustomRadius =
| 'xxs'
| DefaultMantineSize;
declare module '@mantine/core' {
export interface MantineThemeSizesOverride {
spacing: Record<ExtendedCustomSpacing, string>;
radius: Record<ExtendedCustomRadius, string>;
}
}
```
## Other changes
- [Kbd](https://mantine.dev/core/kbd) component now supports `size` prop
- [DateInput](https://mantine.dev/dates/date-input) component no longer supports `preserveTime` prop, use different component to capture time values
- [ScrollArea](https://mantine.dev/core/scroll-area) component no longer has forced `display: table` styles on the wrapper element of the content. It also now supports `content` Styles API selector to apply styles to the content element.
- [Image](https://mantine.dev/core/image) component no longer includes `flex: 0` styles by default
- [SegmentedControl](https://mantine.dev/core/segmented-control) default height values were changed to match sizes of [Input](https://mantine.dev/core/input) components
- Type of `wrapperProps` prop in all components that support it (`Checkbox`, `Radio`, `Chip`, most inputs) was changed to more strict type
- [Portal](https://mantine.dev/core/portal) component now has `reuseTargetNode` prop enabled by default
- [use-form](https://mantine.dev/form/use-form) `setFieldValue` handler types are now stricter
- [Menu.Item](https://mantine.dev/core/menu) no longer has `data-hovered` attribute, use `:hover` and `:focus` selectors instead to apply styles
- [use-os](https://mantine.dev/hooks/use-os) now supports Chrome OS detection, its return type now includes `chromeos` value in the union
- The default eye dropper icon of [ColorInput](https://mantine.dev/core/color-input) component was updated
- The default spacing of [Notification](https://mantine.dev/core/notification) component was increased
## New Contributors
* @mlshvdv made their first contribution in https://github.com/mantinedev/mantine/pull/7703
**Full Changelog**: https://github.com/mantinedev/mantine/compare/7.17.6...8.0.0