Version v7.6.0

Container queries support

You can now use container queries with Mantine components. rem and em functions from postcss-preset-mantine are available in container queries staring from postcss-preset-mantine@1.13.0.

Resize parent element to see container query in action
.root {
  min-width: 200px;
  max-width: 100%;
  min-height: 120px;
  container-type: inline-size;
  overflow: auto;
  resize: horizontal;
}

.child {
  background-color: var(--mantine-color-dimmed);
  color: var(--mantine-color-white);
  padding: var(--mantine-spacing-md);

  @container (max-width: 500px) {
    background-color: var(--mantine-color-blue-filled);
  }

  @container (max-width: 300px) {
    background-color: var(--mantine-color-red-filled);
  }
}

RadarChart component

New RadarChart component:

import { RadarChart } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <RadarChart
      h={300}
      data={data}
      dataKey="product"
      withPolarRadiusAxis
      series={[
        { name: 'Sales January', color: 'lime.4', opacity: 0.1 },
        { name: 'Sales February', color: 'cyan.4', opacity: 0.1 },
      ]}
    />
  );
}

FocusTrap.InitialFocus component

FocusTrap.InitialFocus is a new component that adds a visually hidden element which will receive the focus when the focus trap is activated. Once FocusTrap.InitialFocus loses focus, it is removed from the tab order.

For example, it is useful if you do not want to focus any elements inside the Modal when it is opened:

import { useDisclosure } from '@mantine/hooks';
import { Modal, Button, TextInput, FocusTrap } from '@mantine/core';

function Demo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Modal opened={opened} onClose={close} title="Focus demo">
        <FocusTrap.InitialFocus />
        <TextInput label="First input" placeholder="First input" />
        <TextInput
          data-autofocus
          label="Input with initial focus"
          placeholder="It has data-autofocus attribute"
          mt="md"
        />
      </Modal>

      <Button onClick={open}>Open modal</Button>
    </>
  );
}

New MantineProvider props

MantineProvider now includes more props to control how styles are generated and injected. These props are useful if you use Mantine as a headless library and in test environments.

deduplicateCssVariables

deduplicateCssVariables prop determines whether CSS variables should be deduplicated: if a CSS variable has the same value as in the default theme, it is not added in the runtime. By default, it is set to true. If set to false, all Mantine CSS variables will be added in <style /> tag, even if they have the same value as in the default theme.

import { MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider deduplicateCssVariables={false}>
      {/* Your app here */}
    </MantineProvider>
  );
}

withStaticClasses

withStaticClasses determines whether components should have static classes, for example, mantine-Button-root. By default, static classes are enabled, to disable them set withStaticClasses to false:

import { MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider withStaticClasses={false}>
      {/* Your app here */}
    </MantineProvider>
  );
}

withGlobalClasses

withGlobalClasses determines whether global classes should be added with <style /> tag. Global classes are required for hiddenFrom/visibleFrom and lightHidden/darkHidden props to work. By default, global classes are enabled, to disable them set withGlobalClasses to false. Note that disabling global classes may break styles of some components.

import { MantineProvider } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider withGlobalClasses={false}>
      {/* Your app here */}
    </MantineProvider>
  );
}

HeadlessMantineProvider

HeadlessMantineProvider is an alternative to MantineProvider that should be used when you want to use Mantine as a headless UI library. It removes all features that are related to Mantine styles:

  • Mantine classes are not applied to components
  • Inline CSS variables are not added with style attribute
  • All color scheme related features are removed
  • Global styles are not generated

Limitations of HeadlessMantineProvider:

  • Color scheme switching will not work. If your application has a dark mode, you will need to implement it on your side.
  • Props that are related to styles in all components (color, radius, size, etc.) will have no effect.
  • Some components that rely on styles will become unusable (Grid, SimpleGrid, Container, etc.).
  • lightHidden/darkHidden, visibleFrom/hiddenFrom props will not work.
  • Style props will work only with explicit values, for example mt="xs" will not work, but mt={5} will.

To use HeadlessMantineProvider, follow the getting started guide and replace MantineProvider with HeadlessMantineProvider. Note that you do not need to use ColorSchemeScript in your application, it will not have any effect, you can ignore this part of the guide.

import { HeadlessMantineProvider } from '@mantine/core';

function App() {
  return (
    <HeadlessMantineProvider>
      {/* Your application */}
    </HeadlessMantineProvider>
  );
}

Sparkline trendColors

Sparkline now supports trendColors prop to change chart color depending on the trend. The prop accepts an object with positive, negative and neutral properties:

  • positive - color for positive trend (first value is less than the last value in data array)
  • negative - color for negative trend (first value is greater than the last value in data array)
  • neutral - color for neutral trend (first and last values are equal)

neutral is optional, if not provided, the color will be the same as positive.

Positive trend:

Negative trend:

Neutral trend:

import { Sparkline } from '@mantine/charts';
import { Stack, Text } from '@mantine/core';

const positiveTrend = [10, 20, 40, 20, 40, 10, 50];
const negativeTrend = [50, 40, 20, 40, 20, 40, 10];
const neutralTrend = [10, 20, 40, 20, 40, 10, 50, 5, 10];

function Demo() {
  return (
    <Stack gap="sm">
      <Text>Positive trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={positiveTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />

      <Text mt="md">Negative trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={negativeTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />

      <Text mt="md">Neutral trend:</Text>
      <Sparkline
        w={200}
        h={60}
        data={neutralTrend}
        trendColors={{ positive: 'teal.6', negative: 'red.6', neutral: 'gray.5' }}
        fillOpacity={0.2}
      />
    </Stack>
  );
}

RichTextEditor tasks extension

RichTextEditor now supports tasks tiptap extension:

import TaskItem from '@tiptap/extension-task-item';
import TipTapTaskList from '@tiptap/extension-task-list';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { RichTextEditor, getTaskListExtension } from '@mantine/tiptap';

function Demo() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      getTaskListExtension(TipTapTaskList),
      TaskItem.configure({
        nested: true,
        HTMLAttributes: {
          class: 'test-item',
        },
      }),
    ],
    content: `
        <ul data-type="taskList">
          <li data-type="taskItem" data-checked="true">A list item</li>
          <li data-type="taskItem" data-checked="false">And another one</li>
        </ul>
      `,
  });

  return (
    <div style={{ padding: 40 }}>
      <RichTextEditor editor={editor}>
        <RichTextEditor.Toolbar>
          <RichTextEditor.ControlsGroup>
            <RichTextEditor.TaskList />
            <RichTextEditor.TaskListLift />
            <RichTextEditor.TaskListSink />
          </RichTextEditor.ControlsGroup>
        </RichTextEditor.Toolbar>

        <RichTextEditor.Content />
      </RichTextEditor>
    </div>
  );
}

renderOption prop

Select, MultiSelect, TagsInput and Autocomplete components now support renderOption prop that allows to customize option rendering:

import {
  IconAlignCenter,
  IconAlignJustified,
  IconAlignLeft,
  IconAlignRight,
  IconCheck,
} from '@tabler/icons-react';
import { Group, Select, SelectProps } from '@mantine/core';

const iconProps = {
  stroke: 1.5,
  color: 'currentColor',
  opacity: 0.6,
  size: 18,
};

const icons: Record<string, React.ReactNode> = {
  left: <IconAlignLeft {...iconProps} />,
  center: <IconAlignCenter {...iconProps} />,
  right: <IconAlignRight {...iconProps} />,
  justify: <IconAlignJustified {...iconProps} />,
};

const renderSelectOption: SelectProps['renderOption'] = ({ option, checked }) => (
  <Group flex="1" gap="xs">
    {icons[option.value]}
    {option.label}
    {checked && <IconCheck style={{ marginInlineStart: 'auto' }} {...iconProps} />}
  </Group>
);

function Demo() {
  return (
    <Select
      label="Select with renderOption"
      placeholder="Select text align"
      data={[
        { value: 'left', label: 'Left' },
        { value: 'center', label: 'Center' },
        { value: 'right', label: 'Right' },
        { value: 'justify', label: 'Justify' },
      ]}
      renderOption={renderSelectOption}
    />
  );
}

import { Group, TagsInput, TagsInputProps, Text } from '@mantine/core';

const data: Record<string, { emoji: string; description: string }> = {
  Apples: {
    emoji: '🍎',
    description: 'Crisp and juicy snacking delight',
  },
  Bread: {
    emoji: '🍞',
    description: 'Freshly baked daily essential',
  },
  Bananas: {
    emoji: '🍌',
    description: 'Perfect for a healthy breakfast',
  },
  Eggs: {
    emoji: '🥚',
    description: 'Versatile protein source for cooking',
  },
  Broccoli: {
    emoji: '🥦',
    description: 'Nutrient-rich green vegetable',
  },
};

const renderTagsInputOption: TagsInputProps['renderOption'] = ({ option }) => (
  <Group>
    <Text span fz={24}>
      {data[option.value].emoji}
    </Text>
    <div>
      <Text>{option.value}</Text>
      <Text size="xs" opacity={0.5}>
        {data[option.value].description}
      </Text>
    </div>
  </Group>
);

function Demo() {
  return (
    <TagsInput
      data={['Apples', 'Bread', 'Bananas', 'Eggs', 'Broccoli']}
      renderOption={renderTagsInputOption}
      label="Groceries"
      placeholder="Pick tag from list or type to add new"
      maxDropdownHeight={300}
    />
  );
}

Styles improvements

All Mantine components have been migrated to logical CSS properties (as a replacement for rtl styles) and :where pseudo-class (as a replacement for private CSS variables). These changes should not impact the usage of Mantine components, but now Mantine CSS files have smaller size. For example, @mantine/core/styles.css now has ~ 8% smaller size (192kb -> 177kb).

Pass props to inner recharts components

You can now pass props down to recharts Bar, Area and Line components with barProps, areaProps and lineProps props on BarChart, AreaChart and LineChart components.

All props accept either an object with props or a function that receives series data as an argument and returns an object with props.

import { BarChart } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <BarChart
      h={200}
      data={data}
      dataKey="month"
      orientation="vertical"
      yAxisProps={{ width: 80 }}
      barProps={{ radius: 10 }}
      series={[{ name: 'Smartphones', color: 'blue.6' }]}
    />
  );
}

PieChart percent labels

PieChart now supports percent labels:

Labels position
Labels type
import { PieChart } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return <PieChart withLabelsLine labelsPosition="outside" labelsType="value" withLabels data={data} />;
}

Documentation updates

Help center updates

New articles added to the help center:

Other changes

  • use-list-state hook now supports swap handler
  • form.setFieldValue now supports callback function as an argument
  • px, py, mx and my style props now use logical CSS properties padding-inline, padding-block, margin-inline and margin-block instead of padding-left, padding-right, etc.
  • All components now support me, ms, ps, pe style props to set margin-inline-end, margin-inline-start, padding-inline-start and padding-inline-end CSS properties
  • Tooltip, Popover and other components based on Popover now support floatingStrategy prop to control Floating UI strategy
  • All @mantine/charts components now support children prop, which passes children to the root recharts component
  • use-resize-observer and use-element-size hooks now support ResizeObserver options as hook argument
  • Select, MultiSelect and TagsInput now support onClear prop, the function is called when clear button is clicked
  • MultiSelect and TagsInput now support onRemove prop, the function is called with removed item value when one of the items is deselected
  • Redwood template has been updated to the latest redwood version with Vite