MUIのレイアウト — Box・Stack・Container・Gridの使い分け
この章の目次開く
MUIには、レイアウトのためのコンポーネントが4つあります。役割が少しずつ重なっているので、最初は「どれを使えばいいのか」で迷います。
先に結論を出しておきます。
| コンポーネント | 役割 | 使う場面 |
|---|---|---|
Box | スタイルを当てるための箱 | div の代わり。sx を使いたいとき |
Stack | 一方向に並べる | 縦並び・横並び。要素間の間隔を揃えたいとき |
Container | 中央に寄せて最大幅を決める | ページ全体の外枠 |
Grid | 12分割のグリッドに配置 | カードを3列に並べるなど、2次元の配置 |
学習者Box と Stack ってどっちも箱に見えるんだけど、何が違うの?
Box は「1つの箱」、Stack は「複数を並べる入れ物」です。並べたいものが複数あるなら Stack、単体にスタイルを当てたいだけなら Box と考えてください。
Box — sxを使うための箱
Box は、レンダリングすると <div> になるだけのコンポーネントです。特別な見た目は持ちません。存在理由は sx を使えるようにすることです。
<Box sx={{ p: 2, border: '1px solid', borderColor: 'grey.300', borderRadius: 1 }}>
囲まれた中身
</Box>component propで、実際に出力されるHTMLタグを変えられます。
<Box component="section" sx={{ py: 4 }}>
<Box component="h2" sx={{ m: 0 }}>見出し</Box>
</Box>見た目は sx で決めつつ、HTMLとしての意味は正しく保てます。
Stack — 並べて間隔を揃える
Stack は、子要素を縦か横に並べ、あいだに均等な間隔を空けます。中身はFlexboxです。
<Stack spacing={2}>
<TextField label="名前" />
<TextField label="メールアドレス" />
<Button variant="contained">送信</Button>
</Stack>spacing={2} は sx と同じくspacingの倍数なので、16pxの間隔になります。
横並びにするには direction を変えます。
<Stack direction="row" spacing={1}>
<Button>キャンセル</Button>
<Button variant="contained">保存</Button>
</Stack>レスポンシブにも書けます。狭い画面では縦、広い画面では横、という指定です。
<Stack direction={{ xs: 'column', md: 'row' }} spacing={2}>dividerで区切り線を入れる
要素のあいだに区切り線を挟みたいときは、自分で <Divider /> を並べる必要はありません。
<Stack divider={<Divider flexItem />} spacing={2}>
<Box>1つ目</Box>
<Box>2つ目</Box>
<Box>3つ目</Box>
</Stack>子要素のあいだにだけ挿入されるので、末尾に余計な線が残りません。

Container — 中央寄せと最大幅
Container は、中身を画面中央に寄せ、広がりすぎないように最大幅を設けます。ページ全体の外枠として使います。
<Container maxWidth="md">
<h1>ページタイトル</h1>
</Container>maxWidth にはブレークポイントのキーを渡します。デフォルトは lg です。
| 指定 | 最大幅の目安 |
|---|---|
maxWidth="sm" | 600px |
maxWidth="md" | 900px |
maxWidth="lg" | 1200px(デフォルト) |
maxWidth="xl" | 1536px |
maxWidth={false} | 制限なし |
左右の余白も自動で入るので、スマートフォンで文字が画面端に貼り付くのを防げます。
Grid — v9で書き方が変わった
ここが本章の要注意ポイントです。
学習者<Grid item xs={6}> って書いたら型エラーが出た。記事のとおりに書いたのに…。
v9で Grid の書き方が刷新されました。item と xs は廃止され、size propに一本化されています。
// v8以前。v9では動かない
<Grid container spacing={2}>
<Grid item xs={12} md={6}>左</Grid>
<Grid item xs={12} md={6}>右</Grid>
</Grid>v9では次のように書きます。
<Grid container spacing={2}>
<Grid size={{ xs: 12, md: 6 }}>左</Grid>
<Grid size={{ xs: 12, md: 6 }}>右</Grid>
</Grid>変更点は2つです。
itemを書かない。containerが付いていないGridは自動的にアイテム扱いになる- ブレークポイントごとの幅は
sizeにオブジェクトで渡す。単一の値ならsize={6}でよい
実際に Grid の型が受け取るpropsは container / size / spacing / columns / offset / direction / wrap などで、item も xs も定義されていません。旧v5系の GridLegacy もv9で完全に削除されました。
sizeに渡せる値
| 値 | 意味 |
|---|---|
数値(size={6}) | 12分割のうち何個分か。6なら半分 |
'auto' | 中身の幅に合わせる |
'grow' | 余った幅を埋める |
false | その画面幅では指定しない |
分割数はデフォルトで12ですが、columns で変更できます。
<Grid container columns={10} spacing={2}>
<Grid size={3}>3/10の幅</Grid>
</Grid>縦積みにGridは使わない
v9では direction="column" が使えなくなりました。型も 'row' | 'row-reverse' だけを受け付けます。
どれを選ぶかの判断
先生実務では Stack で大半が片付きます。Grid の出番は「カードを画面幅に応じて3列・2列・1列に変えたい」のような、明確に列を数えたいときだけです。
判断の順番はこうなります。
- 並べたいものが1つだけ、スタイルを当てたい →
Box - 一方向に並べたい →
Stack - 画面幅に応じて列数を変えたい →
Grid - ページ全体の外枠 →
Container
よくあるハマりどころ
<Grid item> が型エラーになる
v9の変更です。item を消して size に書き換えます。
古い記事にあるGridの余白回避策を持ち込んでしまう
<Grid container sx={{ width: 'calc(100% + 16px)' }}> のような調整を書いている記事がありますが、v9では不要です。v9の Grid は spacing をCSSの gap で実装しており(@mui/system の gridGenerator.js で確認できます)、コンテナからはみ出す挙動は起きません。回避策のほうがレイアウトを壊します。
Stackのspacingが効かない
子要素が1つしかない場合、間隔は生まれません。spacing は「あいだ」の指定です。要素の外側に余白が欲しいときは sx={{ p: 2 }} を使います。
ちゃんと使うためのポイント
Boxはsxを使うための箱。divの置き換え-
並べるなら
Stack。縦横の切り替えもdirectionだけで済む Containerはページの外枠。maxWidthのデフォルトはlg-
v9の
Gridは<Grid size={{ xs: 12, md: 6 }}>。itemとxsは廃止 Gridで縦積みはできない。縦はStack
次の章では、文字を扱うTypographyコンポーネントを扱います。見た目とHTMLタグを分けて指定する仕組みが中心です。