この記事について
この記事では React UI ライブラリ Mantine の基本的な使い方について紹介します。
その他、Tailwind CSS と合わせて使う方法についても記載します。
Mantine の特徴
- 100種類以上のカスタマイズ可能なコンポーネント
- 50以上の便利なフック
- TypeScript ベース
- Next.js、Remix などのモダンフレームワークをサポート
- ダークテーマ対応
- スタイルを上書き可能
- 柔軟なテーマ設定
- メンテナ・コミュニティによって構築された豊富なサンプル集 (執筆時点で134種類)
- (個人的に) デフォルトのデザインが美しく、ドキュメントが見やすい
また、スター数は直近3ヶ月で3000近く増えており、リリース速度も速く勢いがあります。
サンプルとして簡単な TODO アプリを作ってみましたが、とても使いやすい印象でした。
https://mantine-demo-one.vercel.app/todo
(コード) https://github.com/RikuOta/mantine-demo
基本
始め方
1. インストール
Getting started に従ってインストールします。(1、2分)
Next.js、Remix などの各種フレームワークを使用する場合は、↑ページ内の Framework guides
を参照。
2. コンポーネントを探す
ドキュメントから、以下のどちらかの方法で目的のコンポーネントを探します。
- 右上の検索フィールドから探す (
Cmd + K
もしくはCtrl + K
でも可) - ナビゲーション内から探す
-
Input
Button
などの基本的なコンポーネントはMANTINE CORE
内にあります
-
3. デザインを調節
より細かい調節をする場合は、Usage 以下のセクションや、component props
タブを参照。
後は import して使うだけです。
import { Button } from '@mantine/core';
function Demo() {
return (
<Button variant="outline" color="teal" size="md">
Settings
</Button>
)
}
テーマ設定
デフォルトのテーマ (色、余白、ブレイクポイントなど) を、デザイン要件に合うよう上書き・追加できます。
手順としては、<App />
などのアプリケーションのルートコンポーネントを <MantineProvider>
でラップし、theme
プロパティに設定を渡す形になります。
詳細は Theme object をご参照ください。
参考までに、独自カラーを追加する例を記載します。
※ Mantine のカラー定義については Colors を参照
import { Button, MantineProvider, MantineThemeOverride } from '@mantine/core'
const theme: MantineThemeOverride = {
// ブランドカラーを追加
colors: {
brand: ['#F0BBDD', '#ED9BCF', '#EC7CC3', '#ED5DB8', '#F13EAF', '#F71FA7', '#FF00A1', '#E00890', '#C50E82', '#AD1374']
},
// ブランドカラーをデフォルトカラーにする
primaryColor: 'brand'
}
function Demo() {
return (
<MantineProvider theme={theme}>
<Button>Primary button</Button>
</MantineProvider>
)
}
型補完を効かせたい場合は、以下のようにします。
import {
...
DefaultMantineColor, // 追加
Tuple // 追加
} from '@mantine/core'
// 追加
type ExtendedCustomColors = 'brand' | DefaultMantineColor;
declare module '@mantine/core' {
export interface MantineThemeColorsOverride {
colors: Record<ExtendedCustomColors, Tuple<string, 10>>;
}
}
// この下は変更なし
...
良い感じ
アイコンの使い方
Mantine にアイコン自体のコンポーネントはないため、Tabler Icons や Radix Icons など、プロジェクトに合ったものを使用してください。
Mantine のドキュメント内でサンプルとして使用されているのは Tabler Icons で、以下のように使用できます。
インストール:
yarn add @tabler/icons
import して使用:
import { IconAward } from '@tabler/icons';
function Demo() {
return (
<IconAward
size={36} // set custom `width` and `height`
color="red" // set `stroke` color
stroke={3} // set `stroke-width`
strokeLinejoin="miter" // override other SVG props
/>
)
}
(補足1) Mantine の色を適用する:
import { useMantineTheme } from '@mantine/core'
import { IconUser } from '@tabler/icons'
function Demo() {
const theme = useMantineTheme()
return (
<IconUser color={theme.colors.gray[6]} />
)
}
(補足2) アイコン付きボタン:
<Button
color="red"
size="md"
leftIcon={<IconTrash size={18} />}
>
Delete
</Button>
余白の取り方
全ての Mantine コンポーネントは、以下のような余白を取るためのプロパティを受け入れます。
※ 詳細は shared-props を参照
- margin
-
m
my
mx
mt
mb
ml
mr
-
- padding
-
p
py
px
pt
pb
pl
pr
-
使用例:
// 下部に lg 分の margin を取る
//
// (補足) lg は `theme.spacing` に定義されています
// { xs: 10, sm: 12, md: 16, lg: 20, xl: 24 }
<Button mb="lg">
Hello
</Button>
注意点として、当然ですが <p>
などの通常の HTML 要素に指定することはできません。
少し冗長ですが、Box や Space を使って書く必要があります。
// 下部に lg 分の margin を取る
<Box component="p" mb="lg">
Hello
</Box>
<p></p>
<Space h="lg" /> // ここに lg 分の高さを取る
<p></p>
(備考)
余白はクラスで指定した方が断然楽なので Tailwind CSS などの他ライブラリに任せるのが良い気がします。
// HTML 要素、Mantine コンポーネント両方で同じように使える
<p className="mb-4">...</p>
<Button className="mb-4">...</Button>
Hooks
以下のような便利なフックが50近く提供されています。
ドキュメントのナビゲーション内 MANTINE HOOKS
に一覧があります。
- 要素サイズ取得 (use-element-size)
- debounce 機能提供 (use-debounced-value)
- 指定時間、ページ内で何もしなかったかどうか検出 (use-idle)
レスポンシブ対応 (メディアクエリ)
メディアクエリはいくつか方法が提供されています。
詳細は Responsive styles をご参照ください。
MediaQuery コンポーネントを使う例:
<MediaQuery smallerThan="lg" styles={{ display: 'none' }}>
<span>画面幅が lg 以上なら表示される</span>
</MediaQuery>
(備考)
Mantine のメディアクエリは使用せず、Tailwind CSS などの他ライブラリに任せた方が簡潔に書ける気がします。
<p className="hidden lg:block">
画面幅が lg 以上なら表示される
</p>
コンポーネントのスタイルを上書きする
各コンポーネントは、クラス or インラインスタイルによる内部の要素全てのスタイル上書きをサポートしています。
カスタマイズオプションと合わせることで、様々なデザイン要件に適合させることができます。
詳細は Styles API をご参照ください。
参考までに Button
のスタイルを上書きする例を記載します。
これを、
こうしたい
まず、Button コンポーネントの Styles API
タブを確認し、Button
がどのようなパーツで構成されているかを確認します。
上記テーブルの Name
列を元に、スタイルを上書きします。
今回は inner
と root
を上書きしています。
<Button
size="md"
variant="subtle"
leftIcon={<IconShoppingCart size={18} />}
// スタイルを上書きする
//
// (補足)
// `styles={theme => ({ ... })}` のように関数にすることで
// テーマオブジェクト (https://mantine.dev/theming/theme-object) にアクセス可能
styles={{
inner: {
position: 'relative',
zIndex: 1
},
root: {
position: 'relative',
border: '2px solid',
borderRadius: '100px',
// 少し右下にズレた背景を作成
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: 0,
zIndex: -1,
width: '100%',
height: '100%',
borderRadius: '100px',
backgroundColor: 'currentColor',
opacity: 0.15,
transform: 'translate(5px, 5px)',
transition: '0.3s'
},
// 既存のホバーアニメーションを削除
'&:hover': {
background: 'none'
},
// ホバーした際、ズレている背景色を正しい位置に戻す
'&:hover::before': {
transform: 'translate(0, 0)'
}
}
}}
>
カートに入れる
</Button>
例えば Vuetify (Vue の UI ライブラリ) だと、SASS 変数や props でどうにもならないとき、
- 開発者ツールを開く
- クラス名を特定
- Deep Selector で上書き
のような手間 & 保守性の低い方法 (アップデートでクラス名が変更されたら崩れる) で上書きしていたため、公式で上書き方法のサポートがあるのは助かっています。
(補足1) IDE の補完
コンポーネントのパーツ構成は IDE の補完でも確認できるため、毎回ドキュメントを見る必要はありません。
(補足2) クラスで上書き
クラスでも上書きできるため、Tailwind CSS のような Utility-First なライブラリとは相性が良いです。
<Button classNames={{
root: 'shadow-md',
label: 'font-mono tracking-wider'
}}>
Hello
</Button>
(補足3) sx プロパティ
root
のみを上書きしたい場合は sx プロパティを使うと簡潔に書けます。
<Text sx={{ color: '#000', fontSize: 18 }}>
Hello
</Text>
(補足4) スタイルを全て削除する
既存のスタイルを全て削除して一から定義したい場合は unstyled プロパティを使うことができます。
<Button unstyled>
Hello
</Button>
Tailwind CSS と合わせて使う
Mantine を単体で使ってみた結果、以下のような点が不都合に感じました。
- 余白を取るのが面倒
-
m
やpb
などの余白プロパティは通常の HTML 要素に指定できない
-
- コンポーネントの調節が props だけで対応できないとき、もしくは Mantine に存在しないコンポーネントを一から作るとき、レスポンシブなスタイルを一から書くのが面倒
UI 自体は Mantine に任せ、余白・レイアウト・スタイリングなどの調節を Tailwind CSS に任せると解決できそうでした。
ただ、以下の設定を Mantine と Tailwind の間で合わせる必要がありましたので、試してみた結果を記載します。
- ベーススタイル
- ブレイクポイント
- 余白 (margin、padding)
- 色
1. ベーススタイル
- Mantine
- normalize.css + 独自のスタイル
- 参考: css-reset-and-global-styles
- Tailwind
- modern-normalize をベースに構築された独自のスタイル
- 参考: Preflight
両方使用すると設定がぶつかるため、片方を無効にします。
UI 構築を任せる Mantine の定義を使用した方が影響は少ないかと思います。
Tailwind の Preflight を無効にする:
module.exports = {
corePlugins: {
preflight: false,
}
}
2. ブレイクポイント
- Mantine のデフォルトのブレイクポイント
- xs:
576px
- sm:
768px
- md:
992px
- lg:
1200px
- xl:
1400px
- 参考: Responsive styles
- xs:
- Tailwind のデフォルトのブレイクポイント
- sm:
640px
- md:
768px
- lg:
1024px
- xl:
1280px
- 2xl:
1536px
- 参考: Responsive Design
- sm:
ブレイクポイントは、メディアクエリ全般を任せる Tailwind の定義を使用しました。
Tailwind のブレイクポイントで Mantine のブレイクポイントを上書きする:
import { MantineProvider, MantineThemeOverride } from '@mantine/core';
import resolveConfig from 'tailwindcss/resolveConfig'
import tailwindConfig from '../tailwind.config.js'
const resolvedTailwindConfig = resolveConfig(tailwindConfig)
// Tailwind のブレイクポイントを Mantine 用に変換
const tailwindScreensForMantine: {[key: string]: number} = {}
const tailwindScreens = resolvedTailwindConfig.theme?.screens
if (tailwindScreens) {
for (const [key, value] of Object.entries(tailwindScreens)) {
// 末尾の px を削除してから数値に変換
tailwindScreensForMantine[key] = Number(value.slice(0, -2))
}
}
const theme: MantineThemeOverride = {
breakpoints: tailwindScreensForMantine
}
function Demo() {
return (
<MantineProvider theme={theme}>
...
</MantineProvider>
)
}
3. 余白
- Mantine のデフォルトの余白
- xs:
10px
- sm:
12px
- md:
16px
- lg:
20px
- xl:
24px
- 参考: Spacing and padding
- xs:
- Tailwind のデフォルトの余白
- 0:
0px
- px:
1px
- 0.5:
0.125rem
- 1:
0.25rem
- ...沢山...
- 96:
24rem
- 参考: default-spacing-scale
- 0:
余白は Tailwind の定義を使おうと思ったのですが、設定を合わせたところ以下のようになってしまい、使用方法で混乱しそうでした。
// 単純な 10px
<Button mb={10} />
// Tailwind の 10 (2.5rem)
<Button mb="10" />
『余白の調節は Tailwind のクラスでのみ行う』のような実装方針でカバーするのが良さそうでした。
// Tailwind の 10 (2.5rem)
<Button className="mb-10" />
4. 色
- Mantine のデフォルトの色
- open-color に若干の追加を行ったカラーパレット
- 参考: Default colors
- Tailwind のデフォルトの色
- 独自のカラーパレット
- 参考: Customizing Colors
色は Tailwind の定義を使ったところ、以下のような使用感になり、微妙な印象でした。
<Badge color="gray.7" className="text-gray-700">
Hello
</Badge>
なので、以下のようにして Mantine の定義を使用することにしました。
(公式の方法ではない点のみ注意)
// Mantine の色を Tailwind 用に変換
//
// 公式の方法がないため、以下のカラーファイルを使用
// https://github.com/mantinedev/mantine/blob/master/src/mantine-styles/src/theme/default-colors.ts
const { DEFAULT_THEME } = require('@mantine/core')
const mantineColorsForTailwind = {}
for (const [name, colors] of Object.entries(DEFAULT_THEME.colors)) {
mantineColorsForTailwind[name] = colors.reduce((acc, cur, i) => {
return { ...acc, [i]: cur }
}, {})
}
module.exports = {
theme: {
colors: mantineColorsForTailwind
}
}
以下のように使用:
<p className="text-dark-0">Hello</p>
<p className="bg-grape-9">World</p>
以上です!
ぜひ使ってみてください〜。