本文へスキップ
ウェブエンジニア問題集
第3章

sx propの使い方 — MUIのスタイリングとstyle・classNameの違い

約9分
この章の目次開く

MUIで「この余白をもう少し広げたい」「この文字だけ赤くしたい」と思ったとき、使うのが sx prop です。MUIのコンポーネントはほぼすべてが sx を受け取ります。

<Box sx={{ mt: 2, p: 3, bgcolor: 'grey.100', borderRadius: 1 }}>
  中身
</Box>
tsx

一見すると独自記法が多くて戸惑いますが、ルールは数えるほどしかありません。この章でまとめて押さえます。

学習者学習者

style={{ marginTop: 16 }} じゃダメなの?わざわざ sx を覚える意味がわからない…。

動きはします。ただ、style では書けないことが3つあります。順に見ていきます。


styleやclassNameとの違い

styleclassNamesx
生成されるものインラインスタイル既存のCSSクラスクラスを自動生成
テーマの値を使える使えない使えない使える
:hover などの擬似クラス書けない書ける書ける
メディアクエリ書けない書ける書ける
MUIのスタイルへの優先度勝つ負けることがある勝つ

style は擬似クラスとメディアクエリが書けません。className は書けますが、別ファイルにCSSを用意する必要があるうえ、MUIが生成するスタイルは詳細度が高いため外から書いたクラスが効かないことがあります。

sx はその両方を回避できる、MUI専用の入り口です。

<Button
  sx={{
    bgcolor: 'primary.main',
    '&:hover': { bgcolor: 'primary.dark' },
    fontSize: { xs: 14, md: 16 },
  }}
>
  送信
</Button>
tsx

擬似クラスもレスポンシブも、このオブジェクトの中で完結します。

ひとつの入口にまとまるイメージ

数値の意味 — 余白は8px刻み

sx で一番よく誤解されるのが数値です。

<Box sx={{ mt: 2 }} />
tsx

これは margin-top: 2px ではなく margin-top: 16px になります。MUIのテーマは spacing という基準値を持っており、デフォルトは8px。余白系の数値はこれに掛け算されます。

書き方実際の値
mt: 18px
mt: 216px
mt: 0.54px
mt: '20px'20px(文字列で渡せばそのまま)

8という数字はMaterial Designの「8dpグリッド」に由来します。ソースにも「Material Designのレイアウトは8dpグリッドに揃えると視覚的に整う」という趣旨のコメントが添えられています。

8倍されるキー、されないキー

すべての数値が8倍されるわけではありません。ここを混同すると事故ります。

キー数値の扱い
m / mt / p / px などの余白系spacingの倍数(8px刻み)
gapspacingの倍数(8px刻み)
width / height1以下なら%、1より大きければpx
fontSizeテーマのtypographyを参照。数値はpx
学習者学習者

width: 1 で100%…。知らずにハマったら絶対に原因わからない…。

先生先生

「数値は意味が変わる、文字列はそのまま」と覚えておくと安全です。迷ったら '16px' のように単位付きの文字列で書けば、変換は一切かかりません。


よく使うショートハンド

sx はCSSプロパティ名をそのまま書けますが、余白と色には短縮形が用意されています。

ショートハンド対応するCSS
m / mt / mr / mb / mlmargin / margin-top / right / bottom / left
mx / my左右の margin / 上下の margin
p / pt / pr / pb / plpadding とその各方向
px / py左右の padding / 上下の padding
bgcolorbackground-color
borderRadiusborder-radius(こちらもspacingの倍数)

ショートハンドを使わず marginTop: 2 と書いても同じように動きます。短縮形は好みで選んで構いません。


テーマの値を参照する

色は生の値を書かず、テーマのキーを文字列で指定できます。

<Box sx={{ color: 'primary.main', bgcolor: 'grey.100' }} />
tsx

primary.main や grey.100 は、テーマの palette を辿るパスです。#1976d2 と直接書くよりこちらが推奨されます。理由は2つあります。

  • テーマ側で色を変えれば、参照している箇所すべてが一度に変わる
  • ダークモードに対応するとき、色の切り替えをテーマに任せられる

より複雑な参照は関数で書きます。

<Box sx={(theme) => ({ boxShadow: theme.shadows[3] })} />
tsx

sx はオブジェクトだけでなく、テーマを受け取って返す関数も渡せます。


レスポンシブに書く

値をオブジェクトにすると、ブレークポイントごとの指定になります。

<Box sx={{ width: { xs: 1, md: 0.5 }, p: { xs: 2, md: 4 } }} />
tsx

画面が狭いときは幅100%・余白16px、md 以上では幅50%・余白32pxという意味です。

デフォルトのブレークポイントは次のとおりです。

キー開始幅想定
xs0pxスマートフォン
sm600pxタブレット
md900px小さめのノートPC
lg1200pxデスクトップ
xl1536px大きな画面

指定した幅以上に適用される、モバイルファーストの考え方です。xs から順に上書きされていきます。詳しい使い分けはレスポンシブ対応の章で扱います。


v9で廃止された書き方

古い記事には、次のような書き方が出てきます。

// v8以前では動いた。v9では動かない
<Box mt={2} p={3} bgcolor="grey.100" />
tsx

sx を経由せず、propとして直接スタイルを渡す書き方で、system props と呼ばれていました。v9でBox・Typography・Grid・Stack・Linkからsystem propsが削除されました。公式のアップグレードガイドに記載があり、実際に Box の型定義からも該当のpropが消えています。

現在は sx にまとめます。

<Box sx={{ mt: 2, p: 3, bgcolor: 'grey.100' }} />
tsx

よくあるハマりどころ

余白が想定の8倍になる

mt: 16 と書いて128pxになるパターンです。pxのつもりで数値を書くと8倍されます。mt: 2 か mt: '16px' が正解です。

width: 1 で画面いっぱいになる

前述のとおり、width は1以下を%として扱います。

sx が効かない自作コンポーネント

sx はMUIのコンポーネントが受け取るpropです。自分で作った素の <div> には効きません。自作コンポーネントで sx を使いたい場合は、styled() で作るか、MUIの Box でラップします。

パフォーマンスが気になる

sx は便利ですが、渡すたびにスタイルを計算します。同じスタイルを何十回も繰り返す箇所では、styled() で作った再利用コンポーネントのほうが有利です。使い分けはカスタマイズの4段階で扱います。


ちゃんと使うためのポイント

  • sx はテーマを知っているスタイル指定の入口。擬似クラスもメディアクエリもここに書ける
  • 余白系の数値はspacing(デフォルト8px)の倍数。mt: 2 は16px
  • width / height は1以下が%。width: 1 は100%
  • 単位付きの文字列('16px')で書けば変換はかからない
  • 色はテーマのパス(primary.main)で参照する
  • v9では <Box mt={2}> は動かない。sx の中に書く

次の章では、sx を踏まえてBox・Stack・Container・Gridでレイアウトを組む方法を扱います。Gridもv9で書き方が変わった部分です。


参考リンク