Logo Vincent
返回文章列表

mantine-ui快速上手

Web
mantine-ui快速上手

【前言】

2023年,前端开发的ui框架应该用哪个呢,

一些基本的诉求:

1.支持pc和移动端

2.支持黑夜&白天主题切换

3.组件丰富

4.支持react

#4是技术栈选择,非通用

按以上要求在github上快速搜索下react相关ui库,

从start数,issues数,最后commit时间等维度做下对比,

这里没有包括老牌的bootstrap,antd,估计大家都审美疲劳了,

1.react-component

https://github.com/react-component

antd团队出品,配合tailwindcss等css框架可以快速定制自己的ui框架,

单纯的antd用太久了,排除

2.chakra-ui

https://github.com/chakra-ui/chakra-ui

各方面都不错,备选

3.mantine-ui

https://github.com/mantinedev/mantine

各方面都不错,组件很丰富,备选

4.material-ui

https://github.com/mui/material-ui

风格太定制化了,排除

5.shadcn

https://github.com/shadcn-ui/ui

也做的不错,组件有点少,排除

6.headlessui

https://github.com/tailwindlabs/headlessui

同做的不错,组件有点少,排除

大家也可以基于自己的诉求筛选下,

最后确定使用mantine-ui,主要是组件太丰富了。

【安装】

mantine-ui分为基础组件和可选组件,

详见: https://mantine.dev/pages/getting-started/

这里先安装基础组件

npm install @mantine/core @mantine/hooks @emotion/react

作为react的ui库,首先要安装react,

npm install react react-dom

【使用】

安装后可以开始写代码来了,

先以button组件为例,

首先需要用mantineProvider包一下root组件,

// react
import React from 'react';
import { createRoot } from 'react-dom/client';

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

/**
 * index view
 */
const IndexView = () => {
  return (
    <MantineProvider withGlobalStyles withNormalizeCSS>
      <div className="container"></div>
    </MantineProvider>
  );
};

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<IndexView />);

然后就可以引入button组件了,

// react
import React from 'react';

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

/**
 * Index
 */
export const Index = () => {
  return <Button>Click me!</Button>;
};

效果如下

代码见:  https://github.com/insistime/mantine-guides/tree/v0.0.2

【浏览器兼容】

mantine支持现代浏览器,不支持ie,

如果需要支持可以介入polyfill,官网文档不是很详细,需要的可以自己研究下,

【常见组件】

这里不是指components,而是常见的业务组件,

可以在这里找到, https://ui.mantine.dev/

例如常见的侧边栏,

及代码

【theme】

主题切换是前端最常见的需求了,

theme在mantine中是一个object,

默认有很多属性,详见: https://mantine.dev/theming/theme-object/

以上属性都可以自定义覆盖掉,

如下代码设置了字体,间距等属性,

<MantineProvider
  theme={{
    // Override any other properties from default theme
    fontFamily: 'Open Sans, sans serif',
    spacing: { xs: '1rem', sm: '1.2rem', md: '1.8rem', lg: '2.2rem', xl: '2.8rem' },
  }}
>
  <div className="container">
    <Index />
  </div>
</MantineProvider>

设置后效果如下,

还有一些比较有意思的变量,简单列一下,

defaultGradient

默认的渐变色

loader

黑夜模式

基于上面的原理,mantine将所有组件适配了白天&黑夜模式,

使用也很方便,如下是黑夜模式

<MantineProvider theme={{ colorScheme: 'dark' }}>
  <div className="container">
    <Index />
  </div>
</MantineProvider>

效果如下

代码见: https://github.com/insistime/mantine-guides/tree/v0.0.3

常见theme函数

createStyles

创建样式

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

const useStyles = createStyles((theme) => ({
  myCustomButton: {
    ...theme.fn.focusStyles(),
  },
}));

useMantineTheme

获取theme

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

function Demo() {
  const theme = useMantineTheme();
  return <div style={{ background: theme.fn.linearGradient(45, 'red', 'blue') }} />;
}

fontStyles

获取字体样式

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

const useStyles = createStyles((theme) => ({
  myCustomText: {
    ...theme.fn.fontStyles(),
  },
}));

function MyCustomText() {
  const { classes } = useStyles();
  return <div className={classes.myCustomText}>My custom text</div>;
}

smallerThan & largerThan

大于或者小于某个尺寸

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

const useStyles = createStyles((theme) => ({
  myResponsiveText: {
    fontSize: theme.fontSizes.md,

    [theme.fn.smallerThan('sm')]: {
      fontSize: theme.fontSizes.sm,
    },

    [theme.fn.smallerThan(500)]: {
      fontSize: theme.fontSizes.xs,
    },
  },
}));

function Demo() {
  const { classes } = useStyles();
  return <div className={classes.myResponsiveText}>My responsive text</div>;
}

gradient

渐变

theme.fn.linearGradient(24, '#000', '#fff'); // -> linear-gradient(24deg, #000 0%, #fff 100%)
theme.fn.linearGradient(133, 'blue', 'red', 'orange', 'cyan', 'white');
// -> linear-gradient(133deg, blue 0%, red 25%, orange 50%, cyan 75%, white 100%)'

theme.fn.radialGradient('#000', '#fff'); // -> radial-gradient(circle, #000 0%, #fff 100%)
theme.fn.radialGradient('blue', 'red', 'orange', 'cyan', 'white');
// -> radial-gradient(circle, blue 0%, red 25%, orange 50%, cyan 75%, white 100%)

更多: https://mantine.dev/theming/functions/

【MantineProvider】

css variables

mantine默认支持css in js的写法,

也支持css variables,用法如下,

mantineProvider上添加withCssVariables属性,

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

function Demo() {
  return (
    <MantineProvider withCSSVariables withGlobalStyles withNormalizeCSS>
      <App />
    </MantineProvider>
  );
}

然后可以在css文件中做对应的修改

.my-button {
  background-color: var(--mantine-color-blue-6);
  font-family: var(--mantine-font-family);
  line-height: var(--mantine-line-height);
}

主题嵌套

mantine的theme可以嵌套,如下

import { Button, MantineProvider, Text } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider theme={{ fontFamily: 'Georgia, serif' }}>
      <Text align="center" mb="xs">
        Georgia or serif text
      </Text>

      <MantineProvider theme={{ fontFamily: 'Greycliff CF, sans-serif' }}>
        <Button>Greycliff CF button</Button>
      </MantineProvider>
    </MantineProvider>
  );
}

styles

也可以直接修改样式,如下

import { MantineProvider, Group, Button, Badge, ButtonStylesParams } from '@mantine/core';

function Demo() {
  return (
    <MantineProvider
      theme={{
        components: {
          Button: {
            // Subscribe to theme and component params
            styles: (theme, params: ButtonStylesParams, { variant }) => ({
              root: {
                height: '2.625rem',
                padding: '0 1.875rem',
                backgroundColor:
                  variant === 'filled'
                    ? theme.colors[params.color || theme.primaryColor][9]
                    : undefined,
              },
            }),
          },

          Badge: {
            // Use raw styles object if you do not need theme dependency
            styles: {
              root: { borderWidth: '0.125rem' },
            },
          },
        },
      }}
    >
      <Group position="center">
        <Button variant="outline">Outline button</Button>
        <Button variant="filled" color="cyan">Filled button</Button>
        <Badge variant="dot">Dot badge</Badge>
      </Group>
    </MantineProvider>
  );
}

classes

也支持添加class,配合类似tailwindcss这种css库使用,

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

function App() {
  return (
    <MantineProvider
      withGlobalStyles
      withNormalizeCSS
      theme={{
        components: {
          Button: {
            classNames: { root: 'button-root', label: 'button-label' },
          },
        },
      }}
    >
      <Button>All Button components will have the classes above</Button>
    </MantineProvider>
  );
}

定义前缀

常见的一些前缀也支持自定义,例如success等,

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

function Demo() {
  return (
    <MantineProvider
      theme={{
        components: {
          Button: {
            variants: {
              danger: (theme) => ({
                root: {
                  backgroundColor: theme.colors.red[9],
                  color: theme.colors.red[0],
                  ...theme.fn.hover({ backgroundColor: theme.colors.red[8] }),
                },
              }),

              success: (theme) => ({
                root: {
                  backgroundImage: theme.fn.linearGradient(
                    45,
                    theme.colors.cyan[theme.fn.primaryShade()],
                    theme.colors.teal[theme.fn.primaryShade()],
                    theme.colors.green[theme.fn.primaryShade()],
                  ),
                  color: theme.white,
                },
              }),
            },
          },
        },
      }}
    >
      <Group position="center">
        <Button variant="danger">Danger variant</Button>
        <Button variant="success">Success variant</Button>
      </Group>
    </MantineProvider>
  );
}

定义大小

支持自定义大小

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

function Demo() {
  return (
    <MantineProvider
      theme={{
        components: {
          Button: {
            sizes: {
              xxxs: () => ({
                root: {
                  height: '1.25rem',
                  padding: '0.3125rem',
                  fontSize: '0.5rem',
                },
              }),

              xxl: (theme) => ({
                root: {
                  fontSize: '1.75rem',
                  height: '5rem',
                  padding: theme.spacing.xl,
                },
              }),
            },
          },
        },
      }}
    >
      <Group position="center">
        <Button size="xxxs">XXXS button</Button>
        <Button size="xxl">XXL button</Button>
      </Group>
    </MantineProvider>
  );
}

定义colors

mantine的色值基于open-color, https://yeun.github.io/open-color/

也可以很方便的自定义色值,详见: https://mantine.dev/theming/colors/

定义字体

theme.fontFamily,修改字体

theme.fontSizes,修改字体大小

【styles】

mantine的样式基于emotion, https://emotion.sh/docs/introduction

emotion是css in js的,先创建一个style.js,如下

// mantine
import { createStyles, getStylesRef, rem } from '@mantine/core';

/**
 * useStyles
 */
export const useStyles = createStyles((theme) => ({
  wrapper: {
    // subscribe to color scheme changes right in your styles
    backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
    maxWidth: rem(400),
    width: '100%',
    height: rem(180),
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    marginLeft: 'auto',
    marginRight: 'auto',
    borderRadius: theme.radius.sm,

    // Dynamic media queries, define breakpoints in theme, use anywhere
    [theme.fn.smallerThan('sm')]: {
      // Child reference in nested selectors via ref
      [`& .${getStylesRef('child')}`]: {
        fontSize: theme.fontSizes.xs,
      },
    },
  },

  child: {
    // assign ref to element
    ref: getStylesRef('child'),
    backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white,
    padding: theme.spacing.md,
    borderRadius: theme.radius.sm,
    boxShadow: theme.shadows.md,
    color: theme.colorScheme === 'dark' ? theme.white : theme.black,
  },
}));

然后在组件中使用,

// react
import React from 'react';

// styles
import { useStyles } from './index-styles.js';

/**
 * Index
 */
export const Index = () => {
  const { classes } = useStyles();
  return (
    <div className={classes.wrapper}>
      <div className={classes.child}>createStyles demo</div>
    </div>
  );
};

效果如下:

代码见: https://github.com/insistime/mantine-guides/tree/v0.0.4

style props

mantine内置了很多style props,

详见: https://mantine.dev/styles/style-props/

【响应式】

mantine支持几种实现响应式的方法

自定义节点

首先可以自定义节点,如果后续响应式依赖节点的话

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

function Demo() {
  return (
    <MantineProvider
      withGlobalStyles
      withNormalizeCSS
      theme={{
        breakpoints: {
          xs: '30em',
          sm: '48em',
          md: '64em',
          lg: '74em',
          xl: '90em',
        },
      }}
    >
      <App />
    </MantineProvider>
  );
}

createStyles

使用createStyles实现响应式,其实就是

1.纯css

2.theme的fn配合节点实现

import { createStyles, getBreakpointValue, rem, em } from '@mantine/core';

const useStyles = createStyles((theme) => ({
  container: {
    height: rem(100),
    backgroundColor: theme.colors.blue[6],

    // Media query with value from theme
    [`@media (max-width: ${em(getBreakpointValue(theme.breakpoints.xl) - 1)})`]: {
      backgroundColor: theme.colors.pink[6],
    },

    // Simplify media query writing with theme functions
    [theme.fn.smallerThan('lg')]: {
      backgroundColor: theme.colors.yellow[6],
    },

    // Static media query
    [`@media (max-width: ${em(800)})`]: {
      backgroundColor: theme.colors.orange[6],
    },
  },
}));

function Demo() {
  const { classes } = useStyles();
  return <div className={classes.container} />;
}

mediaquery

使用mantine的mediaquery组件

<>
  <MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
    <TextInput size="xl" />
  </MediaQuery>

  <MediaQuery largerThan="sm" styles={{ display: 'none' }}>
    <TextInput size="md" />
  </MediaQuery>
</>

inline

纯css实现

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

function Demo() {
  return (
    <TextInput
      sx={(theme) => ({
        background: theme.colors.gray[0],
        padding: theme.spacing.md,

        '@media (max-width: 40em)': {
          padding: theme.spacing.sm,
        },
      })}
    />
  );
}

【黑夜模式】

自定义黑夜模式色值

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

function Demo() {
  return (
    <MantineProvider
      withGlobalStyles
      withNormalizeCSS
      theme={{
        colorScheme: 'dark',
        colors: {
          // override dark colors to change them for all components
          dark: [
            '#d5d7e0',
            '#acaebf',
            '#8c8fa3',
            '#666980',
            '#4d4f66',
            '#34354a',
            '#2b2c3d',
            '#1d1e30',
            '#0c0d21',
            '#01010a',
          ],
        },
      }}
    >
      <App />
    </MantineProvider>
  );
}

自动切换黑夜模式

import { useState } from 'react';
import { MantineProvider, ColorSchemeProvider, ColorScheme } from '@mantine/core';

function Demo() {
  const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
  const toggleColorScheme = (value?: ColorScheme) =>
    setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));

  return (
    <ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
      <MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
        <App />
      </MantineProvider>
    </ColorSchemeProvider>
  );
}

存储到localstorage&快捷键切换

import { MantineProvider, ColorSchemeProvider, ColorScheme } from '@mantine/core';
import { useHotkeys, useLocalStorage } from '@mantine/hooks';

function Demo() {
  const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
    key: 'mantine-color-scheme',
    defaultValue: 'light',
    getInitialValueInEffect: true,
  });

  const toggleColorScheme = (value?: ColorScheme) =>
    setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));

  useHotkeys([['mod+J', () => toggleColorScheme()]]);

  return (
    <ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
      <MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
        <App />
      </MantineProvider>
    </ColorSchemeProvider>
  );
}

跟随用户系统切换

import { useState } from 'react';
import { MantineProvider, ColorSchemeProvider, ColorScheme } from '@mantine/core';
import { useColorScheme } from '@mantine/hooks';

function Demo() {
  // hook will return either 'dark' or 'light' on client
  // and always 'light' during ssr as window.matchMedia is not available
  const preferredColorScheme = useColorScheme();
  const [colorScheme, setColorScheme] = useState<ColorScheme>(preferredColorScheme);
  const toggleColorScheme = (value?: ColorScheme) =>
    setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));

  return (
    <ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
      <MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
        <App />
      </MantineProvider>
    </ColorSchemeProvider>
  );
}

设置到cookie内

// _app.tsx file
import { useState } from 'react';
import NextApp, { AppProps, AppContext } from 'next/app';
import { getCookie, setCookie } from 'cookies-next';
import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core';

export default function App(props: AppProps & { colorScheme: ColorScheme }) {
  const { Component, pageProps } = props;
  const [colorScheme, setColorScheme] = useState<ColorScheme>(props.colorScheme);

  const toggleColorScheme = (value?: ColorScheme) => {
    const nextColorScheme = value || (colorScheme === 'dark' ? 'light' : 'dark');
    setColorScheme(nextColorScheme);
    // when color scheme is updated save it to cookie
    setCookie('mantine-color-scheme', nextColorScheme, { maxAge: 60 * 60 * 24 * 30 });
  };

  return (
    <ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
      <MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
        <Component {...pageProps} />
      </MantineProvider>
    </ColorSchemeProvider>
  );
}

App.getInitialProps = async (appContext: AppContext) => {
  const appProps = await NextApp.getInitialProps(appContext);
  return {
    ...appProps,
    colorScheme: getCookie('mantine-color-scheme', appContext.ctx) || 'light',
  };
};

【多态组件】

有一些组件,底层的实现可以是很多种,

比如button组件,底层可以a标签,可以是nextjs的link组件等,

a标签

import { Button } from '@mantine/core';
import { IconExternalLink } from '@tabler/icons-react';

function Demo() {
  return (
    <Button component="a" href="#" variant="outline" leftIcon={<IconExternalLink size="0.9rem" />}>
      Open in new tab
    </Button>
  );
}

nextjs的link组件

// For Next.js 13 and above
import Link from 'next/link';
import { Button } from '@mantine/core';

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

【hooks】

mantine很贴心的封装好了一些hooks,方便大家使用

详见: https://mantine.dev/hooks/use-counter/

一些比较有意思的摘要出来

防抖

use-debounced-state

use-debounced-value

间隔

use-interval

use-timeout

数组

use-list-state

本地存储

use-local-storage

分页

use-pagination

系统theme

use-color-scheme

快捷键

use-hotkeys

媒体查询

use-media-query

剪贴板

use-clipboard

hash

use-hash

network

use-network

日志

use-logger

【forms】

forms也封装了一些常见的方法

import { TextInput, Checkbox, Button, Group, Box } from '@mantine/core';
import { useForm } from '@mantine/form';

function Demo() {
  const form = useForm({
    initialValues: {
      email: '',
      termsOfService: false,
    },

    validate: {
      email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
    },
  });

  return (
    <Box maw={300} mx="auto">
      <form onSubmit={form.onSubmit((values) => console.log(values))}>
        <TextInput withAsterisk label="Email" placeholder="your@email.com" {...form.getInputProps('email')} />

        <Checkbox
          mt="md"
          label="I agree to sell my privacy"
          {...form.getInputProps('termsOfService', { type: 'checkbox' })}
        />

        <Group position="right" mt="md">
          <Button type="submit">Submit</Button>
        </Group>
      </form>
    </Box>
  );
}

【组件】

mantine组件很丰富,

1.core, https://mantine.dev/core/app-shell/

2.date, https://mantine.dev/dates/dates-provider/

3.其他, https://mantine.dev/others/carousel/

© 2026 vincentqiao.com . 保留所有权利。