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

MUIのレイアウト — Box・Stack・Container・Gridの使い分け

約8分
この章の目次開く

MUIには、レイアウトのためのコンポーネントが4つあります。役割が少しずつ重なっているので、最初は「どれを使えばいいのか」で迷います。

先に結論を出しておきます。

コンポーネント役割使う場面
Boxスタイルを当てるための箱div の代わり。sx を使いたいとき
Stack一方向に並べる縦並び・横並び。要素間の間隔を揃えたいとき
Container中央に寄せて最大幅を決めるページ全体の外枠
Grid12分割のグリッドに配置カードを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>
tsx

component propで、実際に出力されるHTMLタグを変えられます。

<Box component="section" sx={{ py: 4 }}>
  <Box component="h2" sx={{ m: 0 }}>見出し</Box>
</Box>
tsx

見た目は sx で決めつつ、HTMLとしての意味は正しく保てます。


Stack — 並べて間隔を揃える

Stack は、子要素を縦か横に並べ、あいだに均等な間隔を空けます。中身はFlexboxです。

<Stack spacing={2}>
  <TextField label="名前" />
  <TextField label="メールアドレス" />
  <Button variant="contained">送信</Button>
</Stack>
tsx

spacing={2} は sx と同じくspacingの倍数なので、16pxの間隔になります。

横並びにするには direction を変えます。

<Stack direction="row" spacing={1}>
  <Button>キャンセル</Button>
  <Button variant="contained">保存</Button>
</Stack>
tsx

レスポンシブにも書けます。狭い画面では縦、広い画面では横、という指定です。

<Stack direction={{ xs: 'column', md: 'row' }} spacing={2}>
tsx

dividerで区切り線を入れる

要素のあいだに区切り線を挟みたいときは、自分で <Divider /> を並べる必要はありません。

<Stack divider={<Divider flexItem />} spacing={2}>
  <Box>1つ目</Box>
  <Box>2つ目</Box>
  <Box>3つ目</Box>
</Stack>
tsx

子要素のあいだにだけ挿入されるので、末尾に余計な線が残りません。

要素を並べて整えるイメージ

Container — 中央寄せと最大幅

Container は、中身を画面中央に寄せ、広がりすぎないように最大幅を設けます。ページ全体の外枠として使います。

<Container maxWidth="md">
  <h1>ページタイトル</h1>
</Container>
tsx

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>
tsx

v9では次のように書きます。

<Grid container spacing={2}>
  <Grid size={{ xs: 12, md: 6 }}>左</Grid>
  <Grid size={{ xs: 12, md: 6 }}>右</Grid>
</Grid>
tsx

変更点は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>
tsx

縦積みにGridは使わない

v9では direction="column" が使えなくなりました。型も 'row' | 'row-reverse' だけを受け付けます。


どれを選ぶかの判断

先生先生

実務では Stack で大半が片付きます。Grid の出番は「カードを画面幅に応じて3列・2列・1列に変えたい」のような、明確に列を数えたいときだけです。

判断の順番はこうなります。

  1. 並べたいものが1つだけ、スタイルを当てたい → Box
  2. 一方向に並べたい → Stack
  3. 画面幅に応じて列数を変えたい → Grid
  4. ページ全体の外枠 → 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タグを分けて指定する仕組みが中心です。


参考リンク