Variants and sizes

Adding custom variants

Most of Mantine components support variant prop, it can be used in CSS variables resolver, and it is also exposed as data-variant="{value}" attribute on the root element of the component. The easiest way to add custom variants is to add styles that use [data-variant="{value}"].

Example of adding a new variant to the Input component:

  • underline variant styles are added
  • filled variant is a default variant – you do not need to define any additional styles for it
import { Input, MantineProvider, createTheme } from '@mantine/core';
import classes from './Demo.module.css';

// It is better to add new variants in theme.components
// This way you will be able to use them in anywhere in the app
const theme = createTheme({
  components: {
    Input: Input.extend({ classNames: classes }),
  }
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Input variant="underline" placeholder="Underline input" />
      <Input variant="filled" placeholder="Filled input" mt="md" />
    </MantineProvider>
  );
}

Note that you can add custom variants to every Mantine component that supports Styles API even if there are no variants defined on the library side.

Overriding existing variants styles

Apart from adding new variants, you can also override existing ones, for example, you can change the filled variant of the Input component with .input[data-variant="filled"] selector.

Custom variants types

You can define types for custom variants by creating mantine.d.ts file in your project and extending {x}Props interface with the new variant type.

Example of adding custom variant type to Button component:

import { ButtonVariant, MantineSize } from '@mantine/core';

type ExtendedButtonVariant = ButtonVariant | 'contrast' | 'radial-gradient';

declare module '@mantine/core' {
  export interface ButtonProps {
    variant?: ExtendedButtonVariant;
  }
}

variantColorResolver

Button, Badge, ActionIcon and other components support custom variants with variantColorResolver – it supports both changing colors and adding new variants. Note that theme.variantColorResolver is responsible only for colors, if you need to change other properties, use data-variant attribute.

import {
  Button,
  Group,
  MantineProvider,
  defaultVariantColorsResolver,
  VariantColorsResolver,
  parseThemeColor,
  rem,
  rgba,
  darken,
} from '@mantine/core';

const variantColorResolver: VariantColorsResolver = (input) => {
  const defaultResolvedColors = defaultVariantColorsResolver(input);
  const parsedColor = parseThemeColor({
    color: input.color || input.theme.primaryColor,
    theme: input.theme,
  });

  // Override some properties for variant
  if (parsedColor.isThemeColor && parsedColor.color === 'lime' && input.variant === 'filled') {
    return {
      ...defaultResolvedColors,
      color: 'var(--mantine-color-black)',
      hoverColor: 'var(--mantine-color-black)',
    };
  }

  // Completely override variant
  if (input.variant === 'light') {
    return {
      background: rgba(parsedColor.value, 0.1),
      hover: rgba(parsedColor.value, 0.15),
      border: `${rem(1)} solid ${parsedColor.value}`,
      color: darken(parsedColor.value, 0.1),
    };
  }

  // Add new variants support
  if (input.variant === 'danger') {
    return {
      background: 'var(--mantine-color-red-9)',
      hover: 'var(--mantine-color-red-8)',
      color: 'var(--mantine-color-white)',
      border: 'none',
    };
  }

  return defaultResolvedColors;
};

function Demo() {
  return (
    <MantineProvider theme={{ variantColorResolver }}>
      <Group>
        <Button color="lime.4" variant="filled">
          Lime filled button
        </Button>

        <Button color="orange" variant="light">
          Orange light button
        </Button>

        <Button variant="danger">Danger button</Button>
      </Group>
    </MantineProvider>
  );
}

Sizes with components CSS variables

You can add custom sizes to any component that supports size prop by providing a custom CSS variables resolver, usually it is done in theme.components:

import { Button, rem, Group, MantineProvider, createTheme } from '@mantine/core';

const theme = createTheme({
  components: {
    Button: Button.extend({
      vars: (theme, props) => {
        if (props.size === 'xxl') {
          return {
            root: {
              '--button-height': rem(60),
              '--button-padding-x': rem(30),
              '--button-fz': rem(24),
            },
          };
        }

        if (props.size === 'xxs') {
          return {
            root: {
              '--button-height': rem(24),
              '--button-padding-x': rem(10),
              '--button-fz': rem(10),
            },
          };
        }

        return { root: {} };
      },
    }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Group>
        <Button size="xxl">XXL Button</Button>
        <Button size="xxs">XXS Button</Button>
      </Group>
    </MantineProvider>
  );
}

Sizes with data-size attribute

Every component that supports size prop exposes it as data-size="{value}" attribute on the root element. You can use it to add custom sizes:

import { Input, createTheme, MantineProvider } from '@mantine/core';
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    Input: Input.extend({ classNames: classes }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Input placeholder="Size XXL" size="xxl" />
      <Input placeholder="Size XXS" size="xxs" mt="md" />
    </MantineProvider>
  );
}

Sizes with static CSS variables

Mantine components sizes are defined with CSS variables (usually on root element), for example, ActionIcon component has the following CSS variables:

.root {
  --ai-size-xs: rem(18px);
  --ai-size-sm: rem(22px);
  --ai-size-md: rem(28px);
  --ai-size-lg: rem(34px);
  --ai-size-xl: rem(44px);
}

You can override these values with Styles API or add new sizes values:

.root {
  --ai-size-xxs: 16px;
  --ai-size-xxl: 50px;
}

Note that some components have more than one CSS variable for size, for example, the Button component has the following CSS variables:

.root {
  --button-height-xs: rem(30px);
  --button-height-sm: rem(36px);
  --button-height-md: rem(42px);
  --button-height-lg: rem(50px);
  --button-height-xl: rem(60px);

  --button-height-compact-xs: rem(22px);
  --button-height-compact-sm: rem(26px);
  --button-height-compact-md: rem(30px);
  --button-height-compact-lg: rem(34px);
  --button-height-compact-xl: rem(40px);

  --button-padding-x-xs: rem(14px);
  --button-padding-x-sm: rem(18px);
  --button-padding-x-md: rem(22px);
  --button-padding-x-lg: rem(26px);
  --button-padding-x-xl: rem(32px);

  --button-padding-x-compact-xs: rem(7px);
  --button-padding-x-compact-sm: rem(8px);
  --button-padding-x-compact-md: rem(10px);
  --button-padding-x-compact-lg: rem(12px);
  --button-padding-x-compact-xl: rem(14px);
}

Usually, it is more convenient to use data-size attribute or vars on theme to customize sizes in this case.