File: README.md
Mantine
Links
Packages
@mantine/hooksโ collection of 80+ hooks for state and UI management@mantine/coreโ core components library โ 100+ components@mantine/formโ forms management library@mantine/chartsโ recharts based charts library@mantine/notificationsโ a fully featured notifications system@mantine/spotlightโCtrl + Kcommand center for your application@mantine/code-highlightโ code highlight built with highlight.js@mantine/tiptapโ a Tiptap based rich text editor@mantine/dropzoneโ manages files drag 'n' drop to an area or entire screen@mantine/carouselโ Carousel component@mantine/nprogressโ navigation progress@mantine/modalsโ centralized modals manager@mantine/scheduleโ Schedule component for displaying events
Getting help
Mantine has a very friendly community, we are always happy to help you get started:
- Join Discord community โ it is the easiest way to get help, all questions are usually answered in about 30 minutes
- GitHub Discussions โ ask anything about the project or give feedback
Contributors
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:
// 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:
// 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:
// 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,
},
}));// 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.
// 6.x
import { createStyles } from '@mantine/core';
const useStyles = createStyles((theme) => ({
root: {
backgroundColor: theme.colors.red[5],
},
}));/* 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.
// 6.x
import { Box } from '@mantine/core';
function Demo() {
return (
({ backgroundColor: theme.colors.red[5] })} />
);
}// 7.0
import { Box } from '@mantine/core';
function Demo() {
return (
);
}Nested selectors are not supported in the style prop; use className instead:
// 6.x
import { Box } from '@mantine/core';
function Demo() {
return ;
}.box {
&:hover {
background: red;
}
}styles prop
The styles prop no longer supports nested selectors. UseclassNames instead to apply styles to nested elements.
// 6.x โ nested selectors
import { TextInput } from '@mantine/core';
function Demo() {
return (
<TextInput
styles={{
input: {
'&:focus': {
color: 'red',
},
},
}}
/>
);
}/* 7.0 */
.input {
&:focus {
color: red;
}
}Regular selectors are still supported:
// 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.
// 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',
},
})}
/>
);
}/* 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.
// 6.x
import { Box } from '@mantine/core';
function Demo() {
return (
({
backgroundColor: theme.colors.red[6],
color: theme.white,
padding: `calc(${theme.spacing.xl} * 2)`,
})}
/>
);
}/* 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:
// 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,
},
}));/* 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:
// โ No longer includes all global styles
import '@mantine/core/styles/global.css';8.x version import:
// โ
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:
// ๐ 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:
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:
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:
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:
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:
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:
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:
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:
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:
<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 data-hovered attribute
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:
// โ 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:
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,
},
}),
}
});Carousel changes
Starting from the 8.x version, the @mantine/carousel package requiresembla-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:
loopalignslidesToScrolldragFreeinViewThresholdskipSnapscontainScrollspeedanddraggableprops were removed โ they are no longer supported by embla
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:
// โ 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:
// โ 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.
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); // 5lowerFirst
The lowerFirst function is exported from @mantine/hooks.
It converts the first character of a string to lowercase.
import { lowerFirst } from '@mantine/hooks';
lowerFirst('Mantine'); // mantine
lowerFirst('mantine'); // mantineupperFirst
The upperFirst function is exported from @mantine/hooks.
It converts the first character of a string to uppercase.
import { upperFirst } from '@mantine/hooks';
upperFirst('Mantine'); // Mantine
upperFirst('mantine'); // MantinerandomId
The randomId function is exported from @mantine/hooks.
It generates a random id with the mantine- prefix.
import { randomId } from '@mantine/hooks';
randomId(); // mantine-d7h137oav
randomId(); // mantine-1q2j3j4j5range
The range function is exported from @mantine/hooks.
It generates an array of numbers from start to end (inclusive).
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.
import { shallowEqual } from '@mantine/hooks';
shallowEqual({ a: 1 }, { a: 1 }); // true
shallowEqual({ a: 1 }, { a: 2 }); // falsekeys
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[].
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.
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.
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.
import { isElement } from '@mantine/core';
isElement(); // true
isElement('string'); // false
isElement(null); // false
isElement(<></>); // falseisNumberLike
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).
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'); // falsecamelToKebabCase
The camelToKebabCase function is exported from @mantine/core.
It converts a camelCase string to kebab-case.
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.
import { getDefaultZIndex } from '@mantine/core';
getDefaultZIndex('app'); // 100
getDefaultZIndex('modal'); // 200
getDefaultZIndex('popover'); // 300
getDefaultZIndex('overlay'); // 400
getDefaultZIndex('max'); // 9999closeOnEscape
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.
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.
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.
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, []); // 5toDateString
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.
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); // undefinedtoDateTimeString
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.
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); // nullassignTime
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.
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.
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 boundariesFile: 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:
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:
// 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:
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:
// 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:
npm run developCSS modules
By default, Gatsby has different syntax for importing CSS modules:
// 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
sizeprop is usually converted towidthandheightprops under the hood.
If you setsize={16}it will be converted towidth="16"andheight="16"attributes
on the svg element.You can use rem units in the
sizeprop:size="1rem"will be converted towidth="1rem"andheight="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 usecurrentColor 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:
// TypeScript code
import { Button, ButtonProps } from '@mantine/core';
interface MyButtonProps extends ButtonProps {
myProp: string;
}
function MyButton({ myProp, ...others }: MyButtonProps) {
return ;
}// 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:
// ./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:
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:
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:
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:
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:
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:
import { Link } from 'react-router-dom';
import { Button } from '@mantine/core';
function Demo() {
return (
React router link
);
}Polymorphic components as Next.js 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:
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:
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:
import Link from 'next/link';
import { Button } from '@mantine/core';
function Demo() {
return (
<Link href="/hello" {...props} />}>
Typed Next link button
);
}Polymorphic components with react-router NavLink
The react-router-dom NavLink component'sclassName 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:
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:
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:
import { polymorphic, Group, GroupProps } from '@mantine/core';
const PolymorphicGroup = polymorphic<'button', GroupProps>(Group);
function Demo() {
return (
);
}