LoginSignup
62
47

More than 1 year has passed since last update.

React UI ライブラリ『Mantine』の使い方

Last updated at Posted at 2022-11-07

この記事について

この記事では React UI ライブラリ Mantine の基本的な使い方について紹介します。
その他、Tailwind CSS と合わせて使う方法についても記載します。

Mantine の特徴 :rocket:

  1. 100種類以上のカスタマイズ可能なコンポーネント
  2. 50以上の便利なフック
  3. TypeScript ベース
  4. Next.js、Remix などのモダンフレームワークをサポート
  5. ダークテーマ対応
  6. スタイルを上書き可能
  7. 柔軟なテーマ設定
  8. メンテナ・コミュニティによって構築された豊富なサンプル集 (執筆時点で134種類)
  9. (個人的に) デフォルトのデザインが美しく、ドキュメントが見やすい

また、スター数は直近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>>;
  }
}

// この下は変更なし
...

良い感じ :star2:

アイコンの使い方

Mantine にアイコン自体のコンポーネントはないため、Tabler IconsRadix 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 要素に指定することはできません。
少し冗長ですが、BoxSpace を使って書く必要があります。

// 下部に 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 に一覧があります。

レスポンシブ対応 (メディアクエリ)

メディアクエリはいくつか方法が提供されています。
詳細は 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 のスタイルを上書きする例を記載します。
これを、

こうしたい :arrow_down:

まず、Button コンポーネントStyles API タブを確認し、Button がどのようなパーツで構成されているかを確認します。

上記テーブルの Name 列を元に、スタイルを上書きします。
今回は innerroot を上書きしています。

<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 でどうにもならないとき、

  1. 開発者ツールを開く
  2. クラス名を特定
  3. 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 を単体で使ってみた結果、以下のような点が不都合に感じました。

  • 余白を取るのが面倒
    • mpb などの余白プロパティは通常の HTML 要素に指定できない
  • コンポーネントの調節が props だけで対応できないとき、もしくは Mantine に存在しないコンポーネントを一から作るとき、レスポンシブなスタイルを一から書くのが面倒

UI 自体は Mantine に任せ、余白・レイアウト・スタイリングなどの調節を Tailwind CSS に任せると解決できそうでした。

ただ、以下の設定を Mantine と Tailwind の間で合わせる必要がありましたので、試してみた結果を記載します。

  1. ベーススタイル
  2. ブレイクポイント
  3. 余白 (margin、padding)

1. ベーススタイル

両方使用すると設定がぶつかるため、片方を無効にします。
UI 構築を任せる Mantine の定義を使用した方が影響は少ないかと思います。

Tailwind の Preflight を無効にする:

tailwind.config.js
module.exports = {
  corePlugins: {
    preflight: false,
  }
}

2. ブレイクポイント

  • Mantine のデフォルトのブレイクポイント
  • Tailwind のデフォルトのブレイクポイント

ブレイクポイントは、メディアクエリ全般を任せる Tailwind の定義を使用しました。

Tailwind のブレイクポイントで Mantine のブレイクポイントを上書きする:

app.tsx
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 のデフォルトの余白
  • Tailwind のデフォルトの余白

余白は Tailwind の定義を使おうと思ったのですが、設定を合わせたところ以下のようになってしまい、使用方法で混乱しそうでした。

// 単純な 10px
<Button mb={10} />

// Tailwind の 10 (2.5rem)
<Button mb="10" />

『余白の調節は Tailwind のクラスでのみ行う』のような実装方針でカバーするのが良さそうでした。

// Tailwind の 10 (2.5rem)
<Button className="mb-10" />

4. 色

  • Mantine のデフォルトの色
  • Tailwind のデフォルトの色

色は Tailwind の定義を使ったところ、以下のような使用感になり、微妙な印象でした。

<Badge color="gray.7" className="text-gray-700">
  Hello
</Badge>

なので、以下のようにして Mantine の定義を使用することにしました。
(公式の方法ではない点のみ注意)

tailwind.config.js
// 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>

以上です!
ぜひ使ってみてください〜。

62
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
62
47