2021年9月16日に MUI (Material-UI) v5 が正式にリリースされましたMaterial-UI v4 のリリースが 2019年3月なので、約2年半ぶりのメジャーアップデートです。正式名称が Material-UI から MUI に変更になり、パッケージ名も @material-ui/* から @mui/* になりました。

MUI v5 ではコンポーネントのスタイリングの実装方法や記法が一新し、Material-UI v4 以前までの独特な記法から、Theme UIchakra などで用いられている sx Prop によるスタイリングになりました。
大幅な変更になるので、v4 からの移行コストは大きいですが、新たに MUI を学ぶ人には優しくなった印象です。

MUI v5 のスタイリング方法を v4 の記法と比較しながら紹介していきます。

1
yarn add @mui/material @emotion/react @emotion/styled

Introducing MUI Core v5.0
https://mui.com/blog/mui-core-v5/

スタイリングの実装方法

MUI v5 では、スタイリングのコアライブラリが JSS から emotion に変わりました。この記事では説明しないので、詳しく知りたい方は Introducing MUI Core v5.0 を参照してください。

さて、v4 では useStyles という自作フックでコンポーネントのスタイリングをしていましたが、この変更によって v5 からはコンポーネントに sx Prop で直接スタイルを書くことができます。

v4 と v5 の記法の違い

Material-UI v4 の記法

makeStyles でスタイル用のフックを作成し、コンポーネント内で className を取得します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { makeStyles, createStyles } from '@material-ui/core/styles';

// フックを作成
const useStyles = makeStyles((theme) => createStyles({
  root: {
    color: theme.palette.primary.main,
    padding: theme.spacing(2),
  },
}));

function Hoge() {
  // フックで className を取得
  const classes = useStyles();
  return (
    <div className={classes.root}>
      Hoge
    </div>
  );
}

MUI v5 の記法

sx Prop にスタイルを直接記入します。

1
2
3
4
5
6
7
8
9
import Box from '@mui/material/Box';

function Hoge() {
  return (
    <Box sx={{ color: 'primary.main', p: 2 }}>
      Hoge
    </Box>
  );
}

sx Prop によって記述量がとても少なくなり、初心者にもわかりやすい記法になりました。
color に指定した primary.main は、MUI テーマのパレットを利用する方法です。v5 で新たに導入されたこの記法では、theme を引数に取る関数(後述)を指定しなくても MUI テーマのパレットを利用することができます。

MUI System
https://mui.com/system/basics/

Palette
https://mui.com/system/palette/

広告

sx Prop について

MUI v5 のすべてのコンポーネントは sx Prop でスタイリング可能です。sx Prop は従来の(React では非推奨ですが)style Prop に似ていますが、MUI テーマに基づいたスタイリングや、レスポンシブで動的なスタイリングが容易という特徴があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<Box
  sx={{
    width: {
      xs: 100, // theme.breakpoints.up('xs')
      sm: 200, // theme.breakpoints.up('sm')
      md: 300, // theme.breakpoints.up('md')
      lg: 400, // theme.breakpoints.up('lg')
      xl: 500, // theme.breakpoints.up('xl')
    },
    height: (theme) => theme.spacing(10) // => 80px (8px * 10)
  }}
>
  This box has a responsive width.
</Box>

sx Prop では、v4 の makeStyle によるスタイリングと同様に、値にtheme を引数にとった関数を指定することができます。

The sx prop
https://mui.com/system/the-sx-prop/

<Box> コンポーネントについて

<Box> コンポーネントは、sx Prop のプロパティを Prop として直接記述できる基礎的なコンポーネントです。実は v4 時代には既に導入されていましたが、パフォーマンスが低いという難点がありました。リリースノートによると、v5 では大幅にパフォーマンスが改善されたそうです。

1
2
3
4
5
6
7
import Box from '@mui/material/Box';

// padding: 16px (spacing(8px) * 2)
// background-color: theme.palette.primary.dark
<Box p={2} bgcolor="primary.dark" width={100} height={100}>
  hoge
</Box>

<Box> コンポーネントと <Typography> コンポーネントではこの方法で、 sx Prop を使わずにスタイルを設定することができます。

<Box> Component
https://mui.com/components/box/

Properties
https://mui.com/system/properties/

一般の HTML 要素のスタイリング

<div><span> などの一般の HTML 要素は sx Prop を持ちません。これらをスタイリングするには、<Box> コンポーネント や <Typography> コンポーネントを使い、component Prop で HTML 要素を指定するといいでしょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';

interface ItemProps {
  children: React.ReactNode;
}

function Item({ children }: ItemProps) {
  return (
    <Typography variant="body2" component="span" sx={{ backgroundColor: 'secondary.light', p: 1 }}>
      {children}
    </Typography>
  );
}

function Hoge() {
  return (
    // py (padding-top, padding-bottom): { xs: 16px, sm: 8px }
    <Box component="nav" sx={{ py: [2, 1] }}>
      <Stack spacing={2} direction={{ xs: 'column', sm: 'row' }}>
        <Item>Top</Item>
        <Item>News</Item>
        <Item>Site Map</Item>
      </Stack>
    </Box>
  );
}

<Box> Component
https://mui.com/components/box/

<Typography> Component
https://mui.com/components/typography/

margin, padding と spacing という単位

MUI にはスタイリングの統一感を出すために、padding や margin に指定するための spacing という単位が存在します。spacing は MUI テーマ で規定されていて、デフォルトでは 8px に設定されています。

sx Prop や <Box> コンポーネントで設定する padding や margin には、spacing が単位として使われています。v4 では makeStyles でフックを作る際に、theme.spacing を使うことで spacing の単位が明示的に使われていましたが、v5 になって spacing という単位が暗黙的に使われるようになったので注意が必要です。

1
2
3
4
5
// margin-right: 8px (8px * 1)
<Typography component="span" sx={{ mr: 1 }}></Typography>

// padding-top, paading-bottom: 16px (8px * 2)
<Box py={2}></Box>

また、padding と margin だけは spacing を単位に取りますが、その他のプロパティは基本的に px を単位に取るので混同しないように注意が必要です。また、borderRadius は独自の theme.shape.borderRadius (デフォルトは spacing の半分である 4px)という単位を取るので注意しましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 正円になる
<Box sx={{
  display: 'flex',
  width: 64, // => 64px
  height: 64, // => 64px
  borderRadius: 8, // => 32px (4px * 8)
}} />

<Box sx={{
  display: 'flex',
  width: (theme) => theme.spacing(8), // 64px
  height: (theme) => theme.spacing(8), // 64px 
  borderRadius: 8, // 32px (4px * 8)
}}>

Spacing
https://mui.com/system/spacing/

感想

v3 以前の withStyles、v4 の makeStyles といった Material-UI 特有の記法は Material-UI 入門者にはやや難解な印象がありました。
v5 では sx Prop で記述する形になったので、初心者にはわかりやすくなったものの、パレットカラーの記法や、padding, margin における spacing の暗黙的な利用など、覚えなくてはいけない事柄は増えた印象です。とはいえ、この辺りはおそらく Theme UITailwind CSS でも同じことでしょう。

MUI v5 のスタイリングの触りの部分だけをざっと説明しました。
この先は公式ドキュメント、特に MUI System の項を熟読することをお勧めします。

参考

雨のぼり / セカイイチ (2005)

広告