Skip to content

Releases: mantinedev/mantine

9.1.0

21 Apr 16:13

Choose a tag to compare

View changelog with demos on mantine.dev website

Support Mantine development

You can now sponsor Mantine development with OpenCollective.
All funds are used to improve Mantine and create new features and components.

deduplicateInlineStyles

New deduplicateInlineStyles prop on MantineProvider enables
React 19 style tag deduplication for responsive style props.
When many components share the same responsive style prop values, only a single <style />
tag is generated and hoisted to <head /> instead of each component injecting its own:

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

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

This can significantly improve performance when rendering large lists of components
with identical responsive style props. See the
styles performance guide for more details.

use-mask hook

New use-mask hook attaches real-time input masking to any <input> element via
a ref callback. It formats user input against a defined pattern and exposes both the masked display
value and the raw unmasked value. The hook supports built-in and custom tokens, dynamic masks,
character transforms, optional segments, and regex array format:

import { TextInput, Text } from '@mantine/core';
import { useMask } from '@mantine/hooks';

function Demo() {
  const { ref, value, rawValue } = useMask({ mask: '(999) 999-9999' });

  return (
    <>
      <TextInput ref={ref} label="Phone number" placeholder="(___) ___-____" />
      <Text size="sm" mt="sm">Masked value: {value}</Text>
      <Text size="sm">Raw value: {rawValue}</Text>
    </>
  );
}

MaskInput component

New MaskInput component is a wrapper around use-mask hook
that provides all standard input props (label, description, error, etc.) and supports all mask options:

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

function Demo() {
  return (
    <MaskInput
       variant="default" size="sm" radius="md" label="Input label" withAsterisk={false} description="Input description" error=""
      mask="(999) 999-9999"
      placeholder="(___) ___-____"
    />
  );
}

Treemap component

New Treemap component displays hierarchical data as a set of nested
rectangles. It is based on the Treemap recharts component:

// Demo.tsx
import { Treemap } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return <Treemap data={data} />;
}

// data.ts
export const data = [
  {
    name: 'Frontend',
    color: 'blue.8',
    children: [
      { name: 'React', value: 400 },
      { name: 'Vue', value: 200 },
      { name: 'Angular', value: 150 },
    ],
  },
  {
    name: 'Backend',
    color: 'teal.8',
    children: [
      { name: 'Node', value: 300 },
      { name: 'Python', value: 250 },
      { name: 'Go', value: 100 },
    ],
  },
  {
    name: 'Mobile',
    color: 'red.8',
    children: [
      { name: 'React Native', value: 200 },
      { name: 'Flutter', value: 180 },
    ],
  },
];

TimePicker duration type

TimePicker component now supports type="duration" prop that allows
entering durations that exceed 24 hours. In this mode, the hours field has no upper limit
and the input width adjusts dynamically based on the entered value:

import { TimePicker } from '@mantine/dates';

function Demo() {
  return <TimePicker label="Enter duration" type="duration" withSeconds />;
}

Heatmap legend

Heatmap component now supports withLegend prop that displays
a color legend below the chart. Use legendLabels prop to customize labels
(default: ['Less', 'More']):

// Demo.tsx
import { Heatmap } from '@mantine/charts';
import { data } from './data';

function Demo() {
  return (
    <Heatmap
      data={data}
      startDate="2024-02-16"
      endDate="2025-02-16"
      withMonthLabels
      withWeekdayLabels
      withLegend
    />
  );
}

// data.ts
export const data = ${JSON.stringify(data, null, 2)};

MonthPicker and YearPicker presets

MonthPicker and YearPicker components now support
presets prop that allows adding predefined values to pick from. Presets are also available
in MonthPickerInput and YearPickerInput
components:

import dayjs from 'dayjs';
import { MonthPicker } from '@mantine/dates';

function Demo() {
  return (
    <MonthPicker
      presets={[
        { value: dayjs().startOf('month').format('YYYY-MM-DD'), label: 'This month' },
        { value: dayjs().add(1, 'month').startOf('month').format('YYYY-MM-DD'), label: 'Next month' },
        { value: dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD'), label: 'Last month' },
        { value: dayjs().add(6, 'month').startOf('month').format('YYYY-MM-DD'), label: 'In 6 months' },
        { value: dayjs().add(1, 'year').startOf('month').format('YYYY-MM-DD'), label: 'Next year' },
        { value: dayjs().subtract(1, 'year').startOf('month').format('YYYY-MM-DD'), label: 'Last year' },
      ]}
    />
  );
}

use-roving-index hook

New use-roving-index hook implements the
roving tabindex
keyboard navigation pattern. It manages tabIndex state for a group of focusable elements,
handles arrow key navigation with disabled item skipping, and supports both 1D lists and 2D grids:

import { Button, Group } from '@mantine/core';
import { useRovingIndex } from '@mantine/hooks';

const items = ['Bold', 'Italic', 'Underline', 'Strikethrough', 'Code'];

function Demo() {
  const { getItemProps } = useRovingIndex({
    total: items.length,
    orientation: 'horizontal',
    loop: true,
  });

  return (
    <Group gap="xs">
      {items.map((item, index) => (
        <Button key={item} variant="default" {...getItemProps({ index })}>
          {item}
        </Button>
      ))}
    </Group>
  );
}

Tree drag and drop

Tree component now supports drag-and-drop reordering of nodes.
Provide onDragDrop callback to enable it, and use the moveTreeNode utility
to update data based on the result:

import { useState } from 'react';
import { CaretDownIcon } from '@phosphor-icons/react';
import { Group, moveTreeNode, RenderTreeNodePayload, Tree, TreeNodeData } from '@mantine/core';

const data: TreeNodeData[] = [
  {
    label: 'Pages',
    value: 'pages',
    children: [
      { label: 'index.tsx', value: 'pages/index.tsx' },
      { label: 'about.tsx', value: 'pages/about.tsx' },
      { label: 'contact.tsx', value: 'pages/contact.tsx' },
    ],
  },
  {
    label: 'Components',
    value: 'components',
    children: [
      { label: 'Header.tsx', value: 'components/Header.tsx' },
      { label: 'Footer.tsx', value: 'components/Footer.tsx' },
      { label: 'Sidebar.tsx', value: 'components/Sidebar.tsx' },
    ],
  },
  { label: 'package.json', value: 'package.json' },
  { label: 'tsconfig.json', value: 'tsconfig.json' },
];

function Leaf({ node, expanded, hasChildren, elementProps }: RenderTreeNodePayload) {
  return (
    <Group gap={5} {...elementProps}>
      {hasChildren && (
        <CaretDownIcon
          size={18}
          style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
        />
      )}
      <span>{node.label}</span>
    </Group>
  );
}

function Demo() {
  const [treeData, setTreeData] = useState(data);

  return (
    <Tree
      data={treeData}
      onDragDrop={(payload) =>
        setTreeData((current) => moveTreeNode(current, payload))
      }
      renderNode={(payload) => <Leaf {...payload} />}
    />
  );
}

Tree async loading

Tree now supports lazy loading of children. Set hasChildren: true
on a node without providing children – when the node is expanded for the first time,
onLoadChildren callback passed to useTree is called. Use mergeAsyncChildren
utility to splice loaded children into your data:

import { useState } from 'react';
import { CaretDownIcon, SpinnerIcon } from '@phosphor-icons/react';
import {
  Group,
  mergeAsyncChildren,
  RenderTreeNodePayload,
  Tree,
  TreeNodeData,
  useTree,
} from '@mantine/core';

const initialData: TreeNodeData[] = [
  { label: 'Documents', value: 'documents', hasChildren: true },
  { label: 'Photos', value: 'photos', hasChildren: true },
  { label: 'README.md', value: 'readme' },
];

// Simulates an API call to load children
async function fetchChildren(parentValue: string): Promise<TreeNodeData[]> {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return [
    { label: `${parentValue}/file-1.txt`, value: `${parentValue}/file-1.txt` },
    { label: `${parentValue}/file-2.txt`, value: `${parentValue}/file-2.txt` },
    {
      label: `${parentValue}/subfolder`,
      value: `${parentValue}/subfolder`,
      hasChildren: true,
    },
  ];
}

function Leaf({ node, expanded, hasChildre...
Read more

9.0.2

13 Apr 07:58

Choose a tag to compare

What's Changed

  • [@mantine/schedule] Change default events border-radius to sm
  • [@mantine/dates] DateTimePicker: Fix formatting not working with withSeconds set on timePickerProps only
  • [@mantine/core] Textarea: Fix error thrown on resize in some cases
  • [@mantine/modals] Fix modals.closeAll() called from comtext modal causing infinite rerendering
  • [@mantine/tiptap] RichTextEditor: Fix invisible caret in empty task list items
  • [@mantine/schedule] Fix rrule package imports bot being compatible with esm only bundlers
  • [@mantine/schedule] Fix onEventClick called when event is resizing
  • [@mantine/core] Fix incorrect default colors resolver for custom colors in light variant

New Contributors

Full Changelog: 9.0.1...9.0.2

9.0.1

06 Apr 09:01

Choose a tag to compare

What's Changed

  • [@mantine/core] LoadingOverlay: Fix double overlay visible with dark color scheme (#8811)
  • [@mantine/core] RingProgress: Add missing viewBox (#8806)
  • [@mantine/core] Input: Add rootRef prop support
  • [@mantine/core] Combobox: Fix refProp not working on Combobox.Target (#8798)
  • [@mantine/mcp-server] Fix stdio transport to comply with MCP spec (#8792)
  • [@mantine/core] Input: Fix aria-invalid="false" attribute being set (#8785)
  • [@mantine/core] Slider: Fix incorrect orientation inheritance from the parent markup (#8791)
  • [@mantine/core] Fix incorrect default placeholder size in PasswordInput and other components (#8793)
  • [@mantine/core] Badge: Fix text being cut off with some fonts (#8788)
  • [@mantine/hooks] use-scroller: Fix element dynamic resizing not being handled correctly (#8800)
  • [@mantine/core] Fix Checkbox.Group, Switch.Group, Radio.Group and Chip.Group not working with generic primitive values (#8801)
  • [@mantine/core] Popover: Fix missing withProps (#8802)
  • [@mantine/core] Accordion: Fix focus ring being cut off (#8797)
  • [@mantine/charts] Add option to fully customize reference lines label (#8790)
  • [@mantine/core] Fix loading prop not being handled correctly in TagsInput and MultiSelect (#8803)

Full Changelog: 9.0.0...9.0.1

9.0.0 🤩

31 Mar 13:09

Choose a tag to compare

View changelog with demos on mantine.dev website

Migration guide

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.

Peer dependencies requirements updates

Starting from Mantine 9.0, the following dependencies are required:

  • React 19.2+ for all @mantine/* packages
  • Tiptap 3+ for @mantine/tiptap (migration guide)
  • Recharts 3+ for @mantine/charts (no migration required)

New @mantine/schedule package

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.

Schedule

Schedule is a unified container component that combines all views with built-in navigation and view switching. Drag events to reschedule them:

import { useState } from 'react';
import dayjs from 'dayjs';
import { Schedule, ScheduleEventData } from '@mantine/schedule';

const today = dayjs().format('YYYY-MM-DD');
const tomorrow = dayjs().add(1, 'day').format('YYYY-MM-DD');

const initialEvents: ScheduleEventData[] = [
  {
    id: 1,
    title: 'Morning Standup',
    start: `${today} 09:00:00`,
    end: `${today} 09:30:00`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Team Meeting',
    start: `${today} 10:00:00`,
    end: `${today} 11:30:00`,
    color: 'green',
  },
  {
    id: 3,
    title: 'Lunch Break',
    start: `${today} 12:00:00`,
    end: `${today} 13:00:00`,
    color: 'orange',
  },
  {
    id: 4,
    title: 'Code Review',
    start: `${tomorrow} 14:00:00`,
    end: `${tomorrow} 15:00:00`,
    color: 'violet',
  },
  {
    id: 5,
    title: 'Client Call',
    start: `${tomorrow} 15:30:00`,
    end: `${tomorrow} 16:30:00`,
    color: 'cyan',
  },
  {
    id: 6,
    title: 'All Day Conference',
    start: `${today} 00:00:00`,
    end: dayjs(today).add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
    color: 'red',
  },
];

function Demo() {
  const [events, setEvents] = useState(initialEvents);

  const handleEventDrop = ({ eventId, newStart, newEnd }: { eventId: string | number; newStart: string; newEnd: string }) => {
    setEvents((prev) =>
      prev.map((event) =>
        event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
      )
    );
  };

  return (
    <Schedule
      events={events}
      withEventsDragAndDrop
      onEventDrop={handleEventDrop}
    />
  );
}

DayView

DayView displays a single day with configurable time slots, all-day event section, current time indicator, and business hours highlighting. Drag events to reschedule them:

import { useState } from 'react';
import dayjs from 'dayjs';
import { DayView, ScheduleEventData } from '@mantine/schedule';

const today = dayjs().format('YYYY-MM-DD');

const initialEvents: ScheduleEventData[] = [
  {
    id: 1,
    title: 'Morning Standup',
    start: `${today} 09:00:00`,
    end: `${today} 09:30:00`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Team Meeting',
    start: `${today} 11:00:00`,
    end: `${today} 12:00:00`,
    color: 'green',
  },
  {
    id: 3,
    title: 'Code Review',
    start: `${today} 14:00:00`,
    end: `${today} 15:00:00`,
    color: 'violet',
  },
];

function Demo() {
  const [events, setEvents] = useState(initialEvents);

  const handleEventDrop = ({ eventId, newStart, newEnd }: { eventId: string | number; newStart: string; newEnd: string }) => {
    setEvents((prev) =>
      prev.map((event) =>
        event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
      )
    );
  };

  return (
    <DayView
      date={new Date()}
      events={events}
      startTime="08:00:00"
      endTime="18:00:00"
      withEventsDragAndDrop
      onEventDrop={handleEventDrop}
    />
  );
}

WeekView

WeekView shows a weekly calendar grid with time slots, week numbers, weekend day toggling, and multi-day event spanning. Drag events across days and time slots:

import { useState } from 'react';
import dayjs from 'dayjs';
import { WeekView, ScheduleEventData } from '@mantine/schedule';

const today = dayjs().format('YYYY-MM-DD');
const tomorrow = dayjs().add(1, 'day').format('YYYY-MM-DD');

const initialEvents: ScheduleEventData[] = [
  {
    id: 1,
    title: 'Morning Standup',
    start: `${today} 09:00:00`,
    end: `${today} 09:30:00`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Team Meeting',
    start: `${tomorrow} 11:00:00`,
    end: `${tomorrow} 12:00:00`,
    color: 'green',
  },
  {
    id: 3,
    title: 'Code Review',
    start: `${today} 14:00:00`,
    end: `${today} 15:00:00`,
    color: 'violet',
  },
  {
    id: 4,
    title: 'Company Holiday',
    start: dayjs(getStartOfWeek({ date: today, firstDayOfWeek: 1 })).format('YYYY-MM-DD HH:mm:ss'),
    end: dayjs(getStartOfWeek({ date: today, firstDayOfWeek: 1 }))
      .add(2, 'day')
      .format('YYYY-MM-DD HH:mm:ss'),
    color: 'red',
  },
  {
    id: 5,
    title: 'Release Day',
    start: dayjs(getStartOfWeek({ date: today, firstDayOfWeek: 1 })).format('YYYY-MM-DD HH:mm:ss'),
    end: dayjs(getStartOfWeek({ date: today, firstDayOfWeek: 1 }))
      .add(2, 'day')
      .format('YYYY-MM-DD HH:mm:ss'),
    color: 'orange',
  },
];

function Demo() {
  const [events, setEvents] = useState(initialEvents);

  const handleEventDrop = ({ eventId, newStart, newEnd }: { eventId: string | number; newStart: string; newEnd: string }) => {
    setEvents((prev) =>
      prev.map((event) =>
        event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
      )
    );
  };

  return (
    <WeekView
      date={new Date()}
      events={events}
      startTime="08:00:00"
      endTime="18:00:00"
      withEventsDragAndDrop
      onEventDrop={handleEventDrop}
    />
  );
}

MonthView

MonthView displays a monthly calendar grid with event overflow handling, outside days display, and week numbers. Drag events to different days:

import { useState } from 'react';
import dayjs from 'dayjs';
import { MonthView, ScheduleEventData } from '@mantine/schedule';

const today = dayjs().format('YYYY-MM-DD');

const initialEvents: ScheduleEventData[] = [
  {
    id: 1,
    title: 'Team Meeting',
    start: `${today} 09:00:00`,
    end: `${today} 10:30:00`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Project Deadline',
    start: dayjs().add(5, 'day').format('YYYY-MM-DD 00:00:00'),
    end: dayjs().add(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
    color: 'red',
  },
];

function Demo() {
  const [events, setEvents] = useState(initialEvents);

  const handleEventDrop = ({ eventId, newStart, newEnd }: { eventId: string | number; newStart: string; newEnd: string }) => {
    setEvents((prev) =>
      prev.map((event) =>
        event.id === eventId ? { ...event, start: newStart, end: newEnd } : event
      )
    );
  };

  return <MonthView date={new Date()} events={events} withEventsDragAndDrop onEventDrop={handleEventDrop} />;
}

YearView

YearView provides a 12-month year overview organized by quarters with day-level event indicators:

// Demo.tsx
import dayjs from 'dayjs';
import { useState } from 'react';
import { YearView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));

  return (
    <YearView
      date={date}
      onDateChange={setDate}
      events={events}
    />
  );
}

// data.ts
import dayjs from 'dayjs';

const thisYear = dayjs().format('YYYY');

const events = [
  {
    id: 1,
    title: 'New Year',
    start: \\\`\\\${thisYear}-01-01 00:00:00\\\`,
    end: dayjs(\\\`\\\${thisYear}-01-01\\\`).add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
    color: 'blue',
  },
  {
    id: 2,
    title: 'Spring Event',
    start: \\\`\\\${thisYear}-03-15 00:00:00\\\`,
    end: dayjs(\\\`\\\${thisYear}-03-15\\\`).add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
    color: 'green',
  },
];

MobileMonthView

MobileMonthView is a mobile-optimized month view with event details panel for the selected day:

// Demo.tsx
import dayjs from 'dayjs';
import { useState } from 'react';
import { MobileMonthView } from '@mantine/schedule';
import { events } from './data';

function Demo() {
  const [date, setDate] = useState(dayjs().format('YYYY-MM-DD'));
  const [selectedDate, setSelectedDate] = useState<string | null>(dayjs().format('YYYY-MM-DD'));

  return (
    <MobileMonthView
      date={date}
      onDateChange={setDate}
      selectedDate={selectedDate}
      onSelectedDateChange={setSelectedDate}
      events={regularEvents}
    />
  );
}

// data.ts
import dayjs from 'dayjs';

const thisMonth = dayjs().format('YYYY-MM');

export const events = [
  {
    id: 1,
    title: 'Team Meeting',
    start: \`\${thisMonth}-05 09:00:00\`,
    end: \`\${thisMonth}-05 10:00:00\`,
    color: 'blue',
  },
  {
    id: 2,
    title: 'Project Review',
    start: \`\${thisMonth}-05 14:00:00\`,
    end: \`\${thisMonth}-05 15:30:00\`,
    color: 'green',
  },
  {
    id: 3,
    t...
Read more

8.3.18

17 Mar 16:10

Choose a tag to compare

This is the last 8.x release. You are welcome to test 9.0 alpha version and provide feedback before its release on March 31 – https://alpha.mantine.dev/changelog/9-0-0/

  • [@mantine/core] PasswordInput: Fix styles api props not resolving correctly in theme (#8716)

8.3.17

14 Mar 15:53

Choose a tag to compare

Changes

  • [@mantine/core] Stepper: Fix Google Translate compatibility issues (#8744)
  • [@mantine/hooks] use-list-state: Add memoization to all handlers (#8739)

8.3.16

05 Mar 08:21

Choose a tag to compare

What's Changed

  • [@mantine/modals] Fix onClose being called multiple times (#8727)
  • [@mantine/core] Tooltip: Fix component not throwing erro when used with string (#8694)
  • [@mantine/core] NumberInput: Fix incorrect decimal separator parsing in onPaste
  • [@mantine/core] AppShell: Fix layout="alt" not working with mode="static"
  • [@mantine/stotlight] Fix actions list being rendered when nothing found message was not set (#8592)

Full Changelog: 8.3.15...8.3.16

8.3.15

16 Feb 06:50

Choose a tag to compare

What's Changed

  • [@mantine/dropzone] Update react-dropzone to 15.0.0 (#8667)
  • [@mantine/core]TagsInput: Fix duplicate checking bypass with splitChars (#8686)
  • [@mantine/charts] Allow ChartTooltip valueFormatter to return React.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: 8.3.14...8.3.15

9.0.0-alpha.0

12 Feb 14:40

Choose a tag to compare

9.0.0-alpha.0 Pre-release
Pre-release

View changelog with demos on alpha.mantine.dev website

Alpha version

This is the first alpha version of Mantine 9. You are welcome to review changes and provide feedback.

Support Mantine development

You can now sponsor Mantine development with OpenCollective.
All funds are used to improve Mantine and create new features and components.

Migration guide

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.

Peer dependencies requirements updates

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)

Namespace types exports

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

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 canDragEvent callback, and drag-to-select time ranges with withDragSlotSelect
  • Multiple interaction modesdefault mode enables all interactions (click, drag, navigation), static mode renders a read-only calendar
  • Responsive layoutlayout="responsive" automatically switches to YearView and MobileMonthView on smaller screens
  • Custom event rendering – use renderEventBody or renderEvent to 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 labels prop, locale-aware date formatting via dayjs
  • Click handlersonTimeSlotClick, onAllDaySlotClick, onDayClick, and onEventClick for building interactive scheduling interfaces
  • Styles API – full support for classNames, styles, and unstyled props, along with CSS variables for theming

To get started, follow the getting started guide.

Collapse horizontal orientation

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>
  );
}

use-collapse and use-horizontal-collapse hooks

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>
  );
}

use-floating-window hook

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 component

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>
      )}
    </>
  );
}

OverflowList component

New OverflowList component displays list of items and collapses the overflowing items into a single element:

...
Read more

8.3.14

01 Feb 11:50

Choose a tag to compare

What's Changed

  • [@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)

New Contributors

Full Changelog: 8.3.13...8.3.14