mantine Documentation

A fully featured React components library

31,551 stars TypeScript TypeScript 1 file ยท ~11,781 tokens #dark-mode#dark-theme#hooks#react#typescript#uikit
RAW Doc

File: README.md

Mantine








Packages

Getting help

Mantine has a very friendly community, we are always happy to help you get started:

Contributors

Become a contributor

Support development

Mantine is a MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider contributing financially on OpenCollective.

License

MIT


File: apps/mantine.dev/src/pages/guides/6x-to-7x.mdx

import { ThemingDemos } from '@docs/demos';
import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.SixToSeven);

6.x โ†’ 7.x migration guide

This guide will help you migrate your project styles from 6.x to 7.x.
It's not intended to be a comprehensive guide covering all changes in 7.x.
For a complete overview, please see the 7.0.0 changelog.

Migration to @mantine/emotion

The @mantine/emotion package has been available since version 7.9. If you don't want
to use CSS modules, have many styles created with createStyles, sx and styles
props, or simply prefer CSS-in-JS syntax, you can migrate to @mantine/emotion. To view
the full documentation for the @mantine/emotion package, visit this page.

createStyles and Global component

The createStyles function and Global component are no longer available in the @mantine/core package. Change imports
to @mantine/emotion:

tsx
// 6.x
import { createStyles, Global } from '@mantine/core';

// 7.x
import { createStyles, Global } from '@mantine/emotion';

sx and styles props

sx and styles props are available in 7.x the same way as in 6.x after setup:

tsx
// 6.x and 7.x, no changes
import { Box, Button } from '@mantine/core';

function Demo() {
  return (
    <>
       ({ backgroundColor: theme.colors.red[5] })}
      />
      
    </>
  );
}

theme.colorScheme

In v7, the color scheme value is managed by MantineProvider,
and the theme object no longer includes the colorScheme property.
Although it's still possible to access the color scheme value in components with the
useMantineColorScheme hook,
it's not recommended to base your styles on its value. Instead, use the light/dark
utilities.

Example of 6.x createStyles with theme.colorScheme migration to 7.0:

tsx
// 6.x
import { createStyles } from '@mantine/core';

const useStyles = createStyles((theme) => ({
  root: {
    backgroundColor:
      theme.colorScheme === 'dark'
        ? theme.colors.dark[6]
        : theme.colors.gray[0],
    color: theme.colorScheme === 'dark' ? theme.white : theme.black,
  },
}));
tsx
// 7.x
import { createStyles } from '@mantine/emotion';

const useStyles = createStyles((theme, _, u) => ({
  root: {
    [u.dark] {
      backgroundColor: theme.colors.dark[6];
      color: theme.white;
    },

    [u.light]: {
      backgroundColor: theme.colors.gray[0];
      color: theme.black;
    },
  },
}));

Migration to CSS modules

Before getting started, we recommend going through the styles documentation.
The most notable sections are:

Note that this guide assumes you have postcss-preset-mantine installed and configured
in your project.

createStyles

The createStyles function is no longer available in 7.0. Use CSS Modules instead.

tsx
// 6.x
import { createStyles } from '@mantine/core';

const useStyles = createStyles((theme) => ({
  root: {
    backgroundColor: theme.colors.red[5],
  },
}));
scss
/* 7.0 */
.root {
  background-color: var(--mantine-color-red-5);
}

sx prop

The sx prop is no longer available in 7.0. Use className or the style prop instead.

tsx
// 6.x
import { Box } from '@mantine/core';

function Demo() {
  return (
     ({ backgroundColor: theme.colors.red[5] })} />
  );
}
tsx
// 7.0
import { Box } from '@mantine/core';

function Demo() {
  return (
    
  );
}

Nested selectors are not supported in the style prop; use className instead:

tsx
// 6.x
import { Box } from '@mantine/core';

function Demo() {
  return ;
}
scss
.box {
  &:hover {
    background: red;
  }
}

styles prop

The styles prop no longer supports nested selectors. Use
classNames instead to apply styles to nested elements.

tsx
// 6.x โ€“ nested selectors
import { TextInput } from '@mantine/core';

function Demo() {
  return (
    <TextInput
      styles={{
        input: {
          '&:focus': {
            color: 'red',
          },
        },
      }}
    />
  );
}
scss
/* 7.0 */
.input {
  &:focus {
    color: red;
  }
}

Regular selectors are still supported:

tsx
// Works both in 6.x and 7.x
import { TextInput } from '@mantine/core';

function Demo() {
  return (
    <TextInput
      styles={{
        input: {
          color: 'red',
        },
      }}
    />
  );
}

Global styles

The Global component and global styles on the theme are not available in 7.0. Instead,
create a global stylesheet (.css file) and import it at your application entry point.

tsx
// 6.x
import { Global } from '@mantine/core';

function Demo() {
  return (
    <Global
      styles={(theme) => ({
        '*, *::before, *::after': {
          boxSizing: 'border-box',
        },

        body: {
          backgroundColor:
            theme.colorScheme === 'dark'
              ? theme.colors.dark[7]
              : theme.white,
          color:
            theme.colorScheme === 'dark'
              ? theme.colors.dark[0]
              : theme.black,
          lineHeight: theme.lineHeight,
        },

        '.your-class': {
          backgroundColor: 'red',
        },

        '#your-id > [data-active]': {
          backgroundColor: 'pink',
        },
      })}
    />
  );
}
scss
/* 7.0 */
/* src/index.css */
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  background-color: light-dark(
    var(--mantine-color-white),
    var(--mantine-color-dark-7)
  );
  color: light-dark(
    var(--mantine-color-black),
    var(--mantine-color-white)
  );
  line-height: var(--mantine-line-height);
}

.your-class {
  background-color: red;
}

#your-id > [data-active] {
  background-color: pink;
}

theme referencing

All theme properties are now available as CSS variables. We recommend using
CSS variables instead of referencing the theme object in styles.

tsx
// 6.x
import { Box } from '@mantine/core';

function Demo() {
  return (
     ({
        backgroundColor: theme.colors.red[6],
        color: theme.white,
        padding: `calc(${theme.spacing.xl} * 2)`,
      })}
    />
  );
}
scss
/* 7.0 */
.box {
  background-color: var(--mantine-color-red-6);
  color: var(--mantine-color-white);
  padding: calc(var(--mantine-spacing-xl) * 2);
}

theme.colorScheme

The color scheme value is managed by MantineProvider,
and the theme object no longer includes the colorScheme property.
Although it's still possible to access the color scheme value in components with the
useMantineColorScheme hook,
it's not recommended to base your styles on its value. Instead, use the light/dark
mixins or the light-dark CSS function.

Example of 6.x createStyles with theme.colorScheme migration to 7.0:

tsx
// 6.x
import { createStyles } from '@mantine/core';

const useStyles = createStyles((theme) => ({
  root: {
    backgroundColor:
      theme.colorScheme === 'dark'
        ? theme.colors.dark[6]
        : theme.colors.gray[0],
    color: theme.colorScheme === 'dark' ? theme.white : theme.black,
  },
}));
scss
/* 7.0 */

/* With light-dark function */
.root {
  background-color: light-dark(
    var(--mantine-color-gray-0),
    var(--mantine-color-dark-6)
  );
  color: light-dark(
    var(--mantine-color-black),
    var(--mantine-color-white)
  );
}

/* With light/dark mixins */
.root {
  background-color: var(--mantine-color-gray-0);
  color: var(--mantine-color-black);

  @mixin dark {
    background-color: var(--mantine-color-dark-6);
    color: var(--mantine-color-white);
  }
}

Note that if your application has server-side rendering, you should not render any
elements based on its value (more info).
Instead, use the light/dark mixins or the light-dark function to hide/show elements based
on the color scheme value.

Color scheme toggle example:


File: apps/mantine.dev/src/pages/guides/7x-to-8x.mdx

import { PopoverDemos } from '@docs/demos';
import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.SevenToEight);

7.x โ†’ 8.x migration guide

Global styles imports

If you used separate styles imports from @mantine/core/styles/global.css, you need to update imports to use new files.
Note that if you previously imported @mantine/core/styles.css, no changes are required โ€“ all new files are already included in styles.css.

7.x version import:

tsx
// โŒ No longer includes all global styles
import '@mantine/core/styles/global.css';

8.x version import:

tsx
// โœ… Import all global styles separately
import '@mantine/core/styles/baseline.css';
import '@mantine/core/styles/default-css-variables.css';
import '@mantine/core/styles/global.css';

If you used @mantine/core/styles.css, no changes are required;
the import works the same in 7.x and 8.x versions:

tsx
// ๐Ÿ‘ No changes needed if you used styles.css
import '@mantine/core/styles.css';

Portal reuseTargetNode

The reuseTargetNode prop of the Portal component is now enabled by default.
This option improves performance by reusing the target node between portal renders, but
in some edge cases, it might cause issues with z-index stacking context.

If you experience issues with z-index, change the reuseTargetNode prop to false in theme:

tsx
import { createTheme, Portal } from '@mantine/core';

export const theme = createTheme({
  components: {
    Portal: Portal.extend({
      defaultProps: {
        // โœ… Disable reuseTargetNode by default if your application has z-index issues
        reuseTargetNode: false,
      },
    }),
  }
});

Switch withThumbIndicator

The Switch component's default styles have been updated; it now
includes a checked state indicator inside the thumb. If you want to use the
old styles without the indicator, set the withThumbIndicator prop to false in the theme:

tsx
import { createTheme, Switch } from '@mantine/core';

export const theme = createTheme({
  components: {
    Switch: Switch.extend({
      defaultProps: {
        // โœ… Disable withThumbIndicator if you want to use old styles
        withThumbIndicator: false,
      },
    }),
  }
});

Date string values

@mantine/dates components now use date string values in onChange and other callbacks.
If you want to continue using @mantine/dates components the same way as in 7.x, you need
to convert callback values to Date objects:

tsx
import { useState } from 'react';
import { DatePicker } from '@mantine/dates';

export function Demo7x() {
  const [value, setValue] = useState<Date | null>(null);
  // โ›” 7.x โ€“ onChange is called with Date object
  return <DatePicker value={value} onChange={setValue} />
}

export function Demo8x() {
  const [value, setValue] = useState<Date | null>(null);
  // โœ… 8.x โ€“ onChange is called with string date value (for example '1994-08-21')
  // You can either
  // 1. Convert it to Date object to preserve old behavior
  // 2. Update your code to use date string values instead
  return <DatePicker value={value} onChange={val => setValue(new Date(val))} />
}

DatesProvider timezone

The DatesProvider component no longer supports the timezone option:

tsx
import { DatesProvider } from '@mantine/dates';

function Demo7x() {
  // โŒ The timezone option is no longer supported
  return (
    <DatesProvider settings={{ timezone: 'UTC', consistentWeeks: true }}>
      App
    </DatesProvider>
  );
}

function Demo8x() {
  // โœ… Remove the timezone option
  return (
    <DatesProvider settings={{ consistentWeeks: true }}>
      App
    </DatesProvider>
  );
}

If you need to handle timezones in your application, you can use a dedicated date library
(dayjs, luxon, date-fns)
to update timezone values. Example of using Mantine components with dayjs:

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 date library of your choice to assign a timezone
  const dateWithTimeZone = dayjs(value).tz("America/Toronto").toDate();

  return <DatePicker value={value} onChange={setValue} />;
}

DateTimePicker timeInputProps

The DateTimePicker component no longer accepts the timeInputProps prop,
as the underlying TimeInput component was replaced with TimePicker.
To pass props down to the TimePicker component, use the timePickerProps prop instead.

7.x version:

tsx
import { DateTimePicker } from '@mantine/dates';
import { ClockIcon } from '@phosphor-icons/react';

function Demo() {
  return (
    <DateTimePicker
      // โŒ timeInputProps is no longer available
      timeInputProps={{
        leftSection: <ClockIcon size={16} />,
      }}
    />
  );
}

8.x version:

tsx
import { DateTimePicker } from '@mantine/dates';

function Demo() {
  return (
    <DateTimePicker
      // โœ… Use timePickerProps instead of timeInputProps
      timePickerProps={{
        leftSection: <ClockIcon size={16} />,
        minutesStep: 5,
        withDropdown: true,
      }}
    />
  );
}

CodeHighlight usage

The @mantine/code-highlight package no longer depends on highlight.js.
You can follow the updated documentation to set up syntax highlighting with shiki.

If you want to continue using highlight.js in your application,
install the highlight.js package:

Then wrap your app with CodeHighlightAdapterProvider and provide createHighlightJsAdapter as the adapter prop:

tsx
import { MantineProvider } from '@mantine/core';
import { CodeHighlightAdapterProvider, createHighlightJsAdapter } from '@mantine/code-highlight';
import hljs from 'highlight.js/lib/core';
import tsLang from 'highlight.js/lib/languages/typescript';

hljs.registerLanguage('typescript', tsLang);

const highlightJsAdapter = createHighlightJsAdapter(hljs);

function App() {
  return (
    <MantineProvider>
      <CodeHighlightAdapterProvider adapter={highlightJsAdapter}>
        {/* Your app here */}
      </CodeHighlightAdapterProvider>
    </MantineProvider>
  );
}

Then you need to add styles from one of the highlight.js themes to your application.
You can do that by importing a CSS file from the highlight.js package or adding it via a
CDN link to the head of your application:

html
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
/>

After that, you can use the CodeHighlight component in your application the same way you did in the 7.x version.

Menu.Item no longer uses the data-hovered attribute to indicate hovered state.
If you used data-hovered in your styles, you need to change it to :hover and :focus selectors
instead:

scss
// โŒ 7.x โ€“ styles with `data-hovered`,
// no longer works in 8.x
.item {
  &[data-hovered] {
    background-color: red;
  }
}

// โœ… 8.x โ€“ use styles with `:hover` and `:focus`
.item {
  &:hover,
  &:focus {
    background-color: red;
  }
}

Popover hideDetached

Popover now supports the hideDetached prop to automatically close the popover when the target element is removed from the DOM:

<Demo data={PopoverDemos.hideDetached} demoProps={{ defaultExpanded: false }} />

By default, hideDetached is enabled โ€“ the behavior has changed from the 7.x version.
If you prefer to keep the old behavior, you can disable hideDetached for all components:

tsx
import { createTheme, Popover } from '@mantine/core';

export const theme = createTheme({
  components: {
    Popover: Popover.extend({
      defaultProps: {
        // โœ… Disable hideDetached by default
        // if you want to keep the old behavior
        hideDetached: false,
      },
    }),
  }
});

Starting from the 8.x version, the @mantine/carousel package requires
embla-carousel and embla-carousel-react packages with version 8.x.

You need to update embla dependencies:

Update embla props that were previously passed to the Carousel component
to emblaOptions. Full list of props:

  • loop
  • align
  • slidesToScroll
  • dragFree
  • inViewThreshold
  • skipSnaps
  • containScroll
  • speed and draggable props were removed โ€“ they are no longer supported by embla
tsx
import { Carousel } from '@mantine/carousel';

// โŒ 7.x โ€“ embla options passed as props,
// no longer works in 8.x
function Demo7x() {
  return <Carousel loop dragFree align="start" />
}

// โœ… 8.x โ€“ use emblaOptions to pass options to embla
function Demo8x() {
  return <Carousel emblaOptions={{ loop: true, dragFree: true, align: 'start' }} />
}

The useAnimationOffsetEffect hook was removed; it is no longer required, and you need to
remove it from your code:

tsx
// โŒ 7.x โ€“ useAnimationOffsetEffect is no longer available in 8.x
import { Carousel, Embla, useAnimationOffsetEffect } from '@mantine/carousel';

function Demo7x() {
  const [embla, setEmbla] = useState(null);
  useAnimationOffsetEffect(embla, TRANSITION_DURATION);
  return <Carousel getEmblaApi={setEmbla} />;
}

// โœ… 8.x โ€“ remove useAnimationOffsetEffect entirely; it is not required
import { Carousel } from '@mantine/carousel';

function Demo8x() {
  return <Carousel />;
}

The Embla type is no longer exported from the @mantine/carousel package;
you need to change this import to reference the embla-carousel package instead:

tsx
// โŒ 7.x โ€“ The Embla type is no longer available in 8.x
import { Carousel, Embla } from '@mantine/carousel';

function Demo7x() {
  const [embla, setEmbla] = useState(null);
  return <Carousel getEmblaApi={setEmbla} />;
}

// โœ… 8.x โ€“ replace the Embla type import
import { Carousel } from '@mantine/carousel';
import { EmblaCarouselType } from 'embla-carousel';

function Demo8x() {
  const [embla, setEmbla] = useState(null);
  return <Carousel getEmblaApi={setEmbla} />;
}

File: apps/mantine.dev/src/pages/guides/functions-reference.mdx

import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.FunctionsReference);

Functions reference

This guide contains a list of functions exported from Mantine packages that
are not documented elsewhere.

clamp

The clamp function is exported from @mantine/hooks.
It clamps a number within the inclusive lower and upper bounds.

tsx
import { clamp } from '@mantine/hooks';

// With both min and max boundaries
clamp(10, 0, 5); // 5
clamp(100, 0, 5); // 5
clamp(-100, 0, 5); // 0

// With only min boundary
clamp(10, 0, undefined); // 10
clamp(-100, 0, undefined); // 0

// With only max boundary
clamp(0, undefined, 5); // 0
clamp(10, undefined, 5); // 5

lowerFirst

The lowerFirst function is exported from @mantine/hooks.
It converts the first character of a string to lowercase.

tsx
import { lowerFirst } from '@mantine/hooks';

lowerFirst('Mantine'); // mantine
lowerFirst('mantine'); // mantine

upperFirst

The upperFirst function is exported from @mantine/hooks.
It converts the first character of a string to uppercase.

tsx
import { upperFirst } from '@mantine/hooks';

upperFirst('Mantine'); // Mantine
upperFirst('mantine'); // Mantine

randomId

The randomId function is exported from @mantine/hooks.
It generates a random id with the mantine- prefix.

tsx
import { randomId } from '@mantine/hooks';

randomId(); // mantine-d7h137oav
randomId(); // mantine-1q2j3j4j5

range

The range function is exported from @mantine/hooks.
It generates an array of numbers from start to end (inclusive).

tsx
import { range } from '@mantine/hooks';

range(0, 5); // [0, 1, 2, 3, 4, 5]
range(5, 0); // [5, 4, 3, 2, 1, 0]

shallowEqual

The shallowEqual function is exported from @mantine/hooks.
It performs a shallow equality check of two objects.

tsx
import { shallowEqual } from '@mantine/hooks';

shallowEqual({ a: 1 }, { a: 1 }); // true
shallowEqual({ a: 1 }, { a: 2 }); // false

keys

The keys function is exported from @mantine/core.
It is a type-safe wrapper around Object.keys โ€“ it returns
an array of keys typed as (keyof T)[] instead of string[].

tsx
import { keys } from '@mantine/core';

const data = { name: 'Mantine', age: 4 };
const result = keys(data); // ('name' | 'age')[]

deepMerge

The deepMerge function is exported from @mantine/core.
It recursively merges properties of the source object into the target object.
Arrays are not merged โ€“ they are replaced entirely by the source value.

tsx
import { deepMerge } from '@mantine/core';

deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 } });
// { a: 1, b: { c: 2, d: 3 } }

deepMerge({ a: 1 }, { a: 2, b: 3 });
// { a: 2, b: 3 }

filterProps

The filterProps function is exported from @mantine/core.
It removes all undefined properties from the given object.

tsx
import { filterProps } from '@mantine/core';

filterProps({ a: 1, b: undefined, c: 'hello' });
// { a: 1, c: 'hello' }

isElement

The isElement function is exported from @mantine/core.
It is a type guard that checks whether a value is a React element.
Returns false for arrays, null, and React Fragments.

tsx
import { isElement } from '@mantine/core';

isElement(); // true
isElement('string'); // false
isElement(null); // false
isElement(<></>); // false

isNumberLike

The isNumberLike function is exported from @mantine/core.
It checks whether a value is a number or a string that represents
a CSS numeric value (including calc(), var(), and CSS units).

tsx
import { isNumberLike } from '@mantine/core';

isNumberLike(10); // true
isNumberLike('10px'); // true
isNumberLike('1.5rem'); // true
isNumberLike('calc(100% - 10px)'); // true
isNumberLike('var(--size)'); // true
isNumberLike('hello'); // false

camelToKebabCase

The camelToKebabCase function is exported from @mantine/core.
It converts a camelCase string to kebab-case.

tsx
import { camelToKebabCase } from '@mantine/core';

camelToKebabCase('backgroundColor'); // 'background-color'
camelToKebabCase('fontSize'); // 'font-size'
camelToKebabCase('color'); // 'color'

getDefaultZIndex

The getDefaultZIndex function is exported from @mantine/core.
It returns the default z-index value for a given elevation level.

tsx
import { getDefaultZIndex } from '@mantine/core';

getDefaultZIndex('app'); // 100
getDefaultZIndex('modal'); // 200
getDefaultZIndex('popover'); // 300
getDefaultZIndex('overlay'); // 400
getDefaultZIndex('max'); // 9999

closeOnEscape

The closeOnEscape function is exported from @mantine/core.
It creates a keyboard event handler that calls the given callback
when the Escape key is pressed.

tsx
import { closeOnEscape } from '@mantine/core';

// Basic usage
 setOpened(false))} />;

// With options
 setOpened(false), {
    active: isOpened,
    onTrigger: () => console.log('closed'),
  })}
/>;

noop

The noop function is exported from @mantine/core.
It is a no-operation function that does nothing. Useful as a
default callback.

tsx
import { noop } from '@mantine/core';

const onClick = handler || noop;

findClosestNumber

The findClosestNumber function is exported from @mantine/core.
It finds the number in the given array that is closest to the
provided value. Returns the value itself if the array is empty.

tsx
import { findClosestNumber } from '@mantine/core';

findClosestNumber(3, [1, 5, 10]); // 1
findClosestNumber(7, [1, 5, 10]); // 5
findClosestNumber(9, [1, 5, 10]); // 10
findClosestNumber(5, []); // 5

toDateString

The toDateString function is exported from @mantine/dates.
It converts a date value to a YYYY-MM-DD string.
Accepts Date, string, number, and dayjs objects.
Returns null or undefined if the input is null or undefined.

tsx
import { toDateString } from '@mantine/dates';

toDateString(new Date(2025, 0, 15)); // '2025-01-15'
toDateString('2025-01-15 14:30:00'); // '2025-01-15'
toDateString(null); // null
toDateString(undefined); // undefined

toDateTimeString

The toDateTimeString function is exported from @mantine/dates.
It converts a date value to a YYYY-MM-DD HH:mm:ss string.
Accepts Date, string, number, and dayjs objects.
Returns null or undefined if the input is null or undefined.

tsx
import { toDateTimeString } from '@mantine/dates';

toDateTimeString(new Date(2025, 0, 15, 14, 30, 0)); // '2025-01-15 14:30:00'
toDateTimeString('2025-01-15'); // '2025-01-15 00:00:00'
toDateTimeString(null); // null

assignTime

The assignTime function is exported from @mantine/dates.
It assigns a time string in HH:mm:ss format to a date value,
returning a YYYY-MM-DD HH:mm:ss string. If the date value is null,
the current date is used.

tsx
import { assignTime } from '@mantine/dates';

assignTime('2025-01-15', '14:30:00'); // '2025-01-15 14:30:00'
assignTime('2025-01-15 08:00:00', '20:15:30'); // '2025-01-15 20:15:30'
assignTime(null, '10:00:00'); // '[current date] 10:00:00'

clampDate

The clampDate function is exported from @mantine/dates.
It clamps a date between optional min and max boundaries, returning
a YYYY-MM-DD HH:mm:ss string.

tsx
import { clampDate } from '@mantine/dates';

clampDate('2025-01-01', '2025-12-31', '2025-06-15');
// '2025-06-15 00:00:00' โ€“ within range

clampDate('2025-01-01', '2025-12-31', '2024-06-15');
// '2025-01-01 00:00:00' โ€“ clamped to min

clampDate('2025-01-01', '2025-12-31', '2026-06-15');
// '2025-12-31 00:00:00' โ€“ clamped to max

clampDate(undefined, undefined, '2025-06-15');
// '2025-06-15 00:00:00' โ€“ no boundaries

File: apps/mantine.dev/src/pages/guides/gatsby.mdx

import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.Gatsby);

Usage with Gatsby

Generate new application

Follow the Gatsby quick start guide to
create a new Gatsby application:

When asked "Would you like to install a styling system?", select PostCSS.

Installation

PostCSS setup

Install PostCSS plugins and postcss-preset-mantine:

Create a postcss.config.cjs file at the root of your application with the following content:

js
module.exports = {
  plugins: {
    'postcss-preset-mantine': {},
    'postcss-simple-vars': {
      variables: {
        'mantine-breakpoint-xs': '36em',
        'mantine-breakpoint-sm': '48em',
        'mantine-breakpoint-md': '62em',
        'mantine-breakpoint-lg': '75em',
        'mantine-breakpoint-xl': '88em',
      },
    },
  },
};

Setup

Create a src/theme.ts file with your theme override:

tsx
// src/theme.ts
import { createTheme } from '@mantine/core';

export const theme = createTheme({
  fontFamily: 'serif',
  // ... other theme override properties
});

Create a gatsby-ssr.tsx file with the following content:

tsx
import { ColorSchemeScript, MantineProvider } from '@mantine/core';
import { theme } from './src/theme';

export const onPreRenderHTML = ({
  getHeadComponents,
  replaceHeadComponents,
}) => {
  const headComponents = getHeadComponents();
  replaceHeadComponents([
    ...headComponents,
    <ColorSchemeScript key="color-scheme-script" />,
  ]);
};

export const wrapPageElement = ({ element }) => {
  return <MantineProvider theme={theme}>{element}</MantineProvider>;
};

Create a gatsby-browser.tsx file with the following content:

tsx
// Import styles of packages that you've installed.
// All packages except `@mantine/hooks` require styles imports
import '@mantine/core/styles.css';

import { MantineProvider } from '@mantine/core';
import { theme } from './src/theme';

export const wrapPageElement = ({ element }) => {
  return <MantineProvider theme={theme}>{element}</MantineProvider>;
};

All set! Start the development server:

bash
npm run develop

CSS modules

By default, Gatsby has different syntax for importing CSS modules:

tsx
// Default syntax โ€“ will not work in Gatsby
import classes from './Demo.module.css';

// Gatsby syntax
import * as classes from './Demo.module.css';

File: apps/mantine.dev/src/pages/guides/icons.mdx

import { GuidesDemos } from '@docs/demos';
import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.Icons);

Icon libraries with Mantine

You can use any icon library with Mantine components. The most popular options are:

Phosphor icons

Phosphor icons are used in Mantine demos, documentation,
and some @mantine/ packages depend on them. If you don't know which icon library
to use, we recommend Phosphor icons.

Icon size

Most icon libraries support a size prop (or similar width and height props) that allows you to change the
icon width and height. Usually, it's a number in pixels.

rem units in size prop

The icon size prop is usually converted to width and height props under the hood.
If you set size={16} it will be converted to width="16" and height="16" attributes
on the svg element.

You can use rem units in the size prop: size="1rem" will be converted to width="1rem" and height="1rem",
but it's not recommended as it's prohibited by the SVG standard โ€“ some browsers (Firefox) will show a warning in the console.

Custom icons

We recommend using icons as React components. In this case, you'll be able to use
currentColor in the fill and stroke props. This will change the icon color based on the context
it's used in.


File: apps/mantine.dev/src/pages/guides/javascript.mdx

import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.JavaScript);

Usage with JavaScript

Is it possible to use Mantine with JavaScript?

Yes, it's possible to use all @mantine/* packages (as well as all other npm packages) with JavaScript.
@mantine/* packages are written in TypeScript and have type definitions, so you'll get some benefits
of TypeScript (like autocompletion in your IDE) when using them with JavaScript.

Transforming demos code to JavaScript

All demos in Mantine documentation are written in TypeScript. In most cases, there's no difference between
TypeScript and JavaScript code โ€“ you don't have to do anything.

To transform TypeScript code to JavaScript, you can use the TypeScript playground
โ€“ paste the demo code into the playground and all types will be removed. Note that you'll also need
to remove type imports from the code.

Example of transformed code:

tsx
// TypeScript code
import { Button, ButtonProps } from '@mantine/core';

interface MyButtonProps extends ButtonProps {
  myProp: string;
}

function MyButton({ myProp, ...others }: MyButtonProps) {
  return ;
}
tsx
// JavaScript code
import { Button } from '@mantine/core';

function MyButton({ myProp, ...others }) {
  return ;
}

Should Mantine be used with JavaScript?

We recommend using Mantine with TypeScript. It doesn't require deep knowledge of TypeScript
and will make your code more robust and easier to maintain. For example, you'll get type errors
when you pass invalid props to components or when you use non-existent props. TypeScript will also
help you during migration to new versions of Mantine โ€“ you'll get type errors when props/components
that you have in your code are removed/renamed/changed.

If you're not familiar with TypeScript yet, using Mantine with TypeScript will be a great opportunity
to learn it. You can use any of the templates to get started โ€“ all of them include
TypeScript support out of the box.


File: apps/mantine.dev/src/pages/guides/jest.mdx

import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.Jest);

Testing with Jest

This guide will help you set up Jest and React Testing Library for your project.
Note that this guide only covers shared logic that can be applied to any framework, and
it doesn't cover the initial setup of Jest and React Testing Library as it may vary depending on the framework you're using.

Custom render

All Mantine components require MantineProvider to be present in the component tree.
To add MantineProvider to the component tree in your tests, create a custom render
function:

tsx
// ./test-utils/render.tsx
import { render as testingLibraryRender } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
// Import your theme object
import { theme } from '../src/theme';

export function render(ui: React.ReactNode) {
  return testingLibraryRender(<>{ui}</>, {
    wrapper: ({ children }: { children: React.ReactNode }) => (
      <MantineProvider theme={theme} env="test">{children}</MantineProvider>
    ),
  });
}

It's usually more convenient to export all @testing-library/* functions that you're planning to use
from a ./testing-utils/index.ts file:

tsx
import userEvent from '@testing-library/user-event';

export * from '@testing-library/react';
export { render } from './render';
export { userEvent };

Then you should import all testing utilities from ./testing-utils instead of @testing-library/react:

tsx
import { render, screen } from '../test-utils';
import { Welcome } from './Welcome';

describe('Welcome component', () => {
  it('has correct Next.js theming section link', () => {
    render(<Welcome />);
    expect(screen.getByText('this guide')).toHaveAttribute(
      'href',
      'https://mantine.dev/guides/next/'
    );
  });
});

Mock Web APIs

Most Mantine components depend on browser APIs like window.matchMedia or ResizeObserver.
These APIs aren't available in the jest-environment-jsdom environment and you'll need to mock them in your tests.

Create a jest.setup.js file in your project root and add the following code to it:

tsx
import '@testing-library/jest-dom';

const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
window.HTMLElement.prototype.scrollIntoView = () => {};

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

if (!document.fonts) {
  Object.defineProperty(document, 'fonts', {
    writable: true,
    value: { addEventListener: jest.fn(), removeEventListener: jest.fn() },
  });
}

class ResizeObserver {
  observe() {}
  unobserve() {}
  disconnect() {}
}

window.ResizeObserver = ResizeObserver;

Then add it as a setup file in your jest.config.js:

js
const config = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  // ... rest of your config
};

Framework specific setup

Jest setup for different frameworks may vary and usually changes over time.
To learn how to set up Jest for your framework, either check the Jest
and React Testing Library documentation
or check one of the premade templates. Most of the templates include Jest setup, and
you can use them as a reference.

Testing examples

You can find testing examples in Mantine Help Center:


File: apps/mantine.dev/src/pages/guides/polymorphic.mdx

import { GuidesDemos } from '@docs/demos';
import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.Polymorphic);

Polymorphic components

What is a polymorphic component

A polymorphic component is a component whose root element can be changed with the component prop.
All polymorphic components have a default element that's used when the component prop is not provided.
For example, the Button component's default element is button and
it can be changed to a or any other element or component:

renderRoot prop

renderRoot is an alternative to the component prop, which accepts a function that should return
a React element. It is useful in cases when component prop cannot be used, for example,
when the component that you want to pass to the component is generic
(accepts type or infers it from props, for example <Link<'/'> />).

Example of using renderRoot prop, the result is the same as in the previous demo:

tsx
import { Button } from '@mantine/core';

function Demo() {
  return (
     (
        <a href="https://mantine.dev/" target="_blank" {...props} />
      )}
    >
      Mantine website
    
  );
}

!important It's required to spread the props argument into the root element. Otherwise,
there will be no styles and the component might not be accessible.

Polymorphic components as other React components

You can pass any other React component to the component prop.
For example, you can pass the Link component from react-router-dom:

tsx
import { Link } from 'react-router-dom';
import { Button } from '@mantine/core';

function Demo() {
  return (
    
      React router link
    
  );
}

The Next.js link doesn't work in the same way as other similar components in all Next.js versions.

With Next.js 12 and below:

tsx
import Link from 'next/link';
import { Button } from '@mantine/core';

function Demo() {
  return (
    <Link href="/hello" passHref>
      Next link button
    </Link>
  );
}

With Next.js 13 and above:

tsx
import Link from 'next/link';
import { Button } from '@mantine/core';

function Demo() {
  return (
    
      Next link button
    
  );
}

Polymorphic components with generic components

You cannot pass generic components to the component prop because it's not possible to infer generic types
from the component prop. For example, you cannot pass typed Next.js Link
to the component prop because it's not possible to infer the href type from the component prop. The component itself
will work correctly, but you'll have a TypeScript error.

To make generic components work with polymorphic components, use the renderRoot prop instead of component:

tsx
import Link from 'next/link';
import { Button } from '@mantine/core';

function Demo() {
  return (
     <Link href="/hello" {...props} />}>
      Typed Next link button
    
  );
}

The react-router-dom NavLink component's
className prop accepts a function based on which you can add an active class to the link. This feature is
incompatible with Mantine's component prop, but you can use the renderRoot prop instead:

tsx
import cx from 'clsx';
import { NavLink } from 'react-router-dom';
import { Button } from '@mantine/core';

function Demo() {
  return (
     (
        <NavLink
          className={({ isActive }) =>
            cx(className, { 'active-class': isActive })
          }
          {...others}
        />
      )}
    >
      React router NavLink
    
  );
}

Wrapping polymorphic components

Non-polymorphic components include React.ComponentProps<'x'> as part of their props type,
where x is the root element of the component. For example, the Container component
is not polymorphic โ€“ its root element is always div, so its props type includes React.ComponentProps<'div'>.

Polymorphic components don't include React.ComponentProps<'x'> as part of their props type
because their root element can be changed, and thus the props type can be inferred only after the component was rendered.

Example of creating a non-polymorphic wrapper component for Mantine polymorphic component:

Example of creating a polymorphic wrapper component for Mantine polymorphic component:

Dynamic component prop

You can use a dynamic value in the component prop, but in this case, you need to either provide types manually
or disable type checking by passing any as a type argument to the polymorphic component:

tsx
import { Box } from '@mantine/core';

function KeepTypes() {
  return (
    
      component={(Math.random() > 0.5 ? 'input' : 'div') as any}
    />
  );
}

function NukeTypes() {
  return (
     component={Math.random() > 0.5 ? 'input' : 'div'} />
  );
}

Create your own polymorphic components

Use the polymorphic function and Box component to create new polymorphic components:

Make Mantine component polymorphic

Polymorphic components have a performance overhead for tsserver (no impact on runtime performance),
because of that, not all Mantine components have polymorphic types, but all components still
accept the component prop โ€“ the root element can be changed.

To make a Mantine component polymorphic, use the polymorphic function the same way
as in the previous example:

tsx
import { polymorphic, Group, GroupProps } from '@mantine/core';

const PolymorphicGroup = polymorphic<'button', GroupProps>(Group);

function Demo() {
  return (
    
  );
}