- 技術 💻
FlexboxとGridの使い分け|同じUIを両方で作り3つの画面幅で実測比較
FlexboxとGridの使い分けは、「中身の大きさに合わせて1方向に並べるならFlexbox、先に行と列の枠を決めて2方向にそろえるならGrid」です。 どちらが上ということはなく、1つの画面の中で両方を組み合わせるのが普通です。
ただ、この一文だけでは現場で迷います。そこでこの記事では、ヘッダー・カード一覧・フォーム・聖杯レイアウト(ヘッダー/3カラム/フッター)をFlexboxとGridの両方で同じ見た目を目指して実装し、Playwright(Chromium 141)で画面幅320px・768px・1280pxのときの要素の位置と幅を getBoundingClientRect() で測りました。「どちらでも作れるが、どこで差が出るか」を数値で示します。あわせて、gap、auto-fit/minmax()、subgrid、コンテナクエリ、よくある崩れ(flex-shrink、min-width: 0)も実測つきで整理します。
FlexboxとGridの使い分け早見表
| 観点 | Flexbox | Grid |
|---|---|---|
| 並べる方向 | 1次元(行か列のどちらか) | 2次元(行と列を同時に) |
| サイズの決め方 | 中身が先。要素の大きさから並びが決まる | 枠が先。トラック(行・列)を決めてから入れる |
| 折り返した後の最終行 | 残った要素が伸びて横幅いっぱいになりやすい | 列幅がそろったまま |
| 得意なUI | ナビ、ボタン群、タグ、フォームの1行、中央寄せ | カード一覧、フォーム全体、ページ骨格、ダッシュボード |
| 子の子をそろえる | 苦手(入れ子ごとに独立) | subgridで親の行・列にそろえられる |
| 要素の余白 | gap が使える |
gap が使える |
| 崩れやすい点 | flex-shrink で縮む、min-width: auto ではみ出す |
1fr の最小幅が中身に引っぱられてはみ出す |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「FlexboxとGridの使い分け|同じUIを両方で作り3つの画面幅で実測比較」
まず判断フロー:迷ったらこの順に考える
「行と列の両方をそろえたいか」を最初に問うと、ほとんど迷いません。
flowchart TD
A[並べたい要素がある] --> B{行と列の両方で<br>位置をそろえたい?}
B -- はい --> G[Grid]
B -- いいえ --> C{要素の数や幅が<br>中身しだいで変わる?}
C -- はい --> F[Flexbox]
C -- いいえ --> D{折り返した最終行も<br>同じ幅にしたい?}
D -- はい --> G
D -- いいえ --> F
G --> E{子要素の中身も<br>親の列・行にそろえたい?}
E -- はい --> S[Grid + subgrid]
E -- いいえ --> G2[Grid のまま]
| よくある迷い | おすすめ | 理由 |
|---|---|---|
| ナビの項目数がページごとに違う | Flexbox | 中身の幅に合わせて自然に並ぶ |
| カードを何列でも同じ幅で並べたい | Grid(auto-fill/auto-fit) |
最終行の幅もそろう |
| ラベルと入力欄の縦位置をそろえたい | Grid | ラベル列の幅を一番長いラベルに合わせられる |
| ボタンを右端に寄せたい | Flexbox(margin-left: auto) |
1行の中の配置だけで済む |
| ページ全体の骨格 | Grid(grid-template-areas) |
HTMLの入れ子を増やさずに配置を変えられる |
検証の条件
実測はすべて同じHTML・同じCSSで、画面幅だけを変えて行いました。
| 項目 | 内容 |
|---|---|
| ブラウザ | Chromium 141.0.7390.37(Playwright 1.56.1、ヘッドレス) |
| 画面幅 | 320px / 768px / 1280px(高さ900px) |
| 測り方 | getBoundingClientRect() の x・幅・高さを整数に丸めて記録 |
| 並べる領域の幅 | 270px / 718px / 1230px(bodyの余白16pxと枠の分を引いた幅) |
以下の表の数値は、この条件で実際に出力された値です。
1. ヘッダー:Flexboxが基本、ただし縮みに注意
ロゴ(幅120pxのブロック)・ナビ3項目・問い合わせボタンの横並びです。3通り用意しました。
/* A: Flexbox(基本形) */
.h-flex { display: flex; align-items: center; gap: 16px; }
.h-flex nav { display: flex; gap: 8px; margin-left: auto; } /* ナビ以降を右へ */
/* B: Flexbox(直した形) */
.h-flex-fix { flex-wrap: wrap; }
.h-flex-fix .logo { flex-shrink: 0; } /* ロゴは縮ませない */
/* C: Grid */
.h-grid { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16px; }
実測結果(ロゴ幅/ヘッダーの高さ/はみ出し量)
| 画面幅 | A: Flexbox基本 | B: Flexbox直した形 | C: Grid |
|---|---|---|---|
| 320px | ロゴ 0px /36px/はみ出し 138px | ロゴ120px/132px(3段に折り返し)/0px | ロゴ120px/36px/はみ出し 258px |
| 768px | 120px/36px/0px | 120px/36px/0px | 120px/36px/0px |
| 1280px | 120px/36px/0px | 120px/36px/0px | 120px/36px/0px |
わかったことは3つです。
- Flexboxの子は、既定で
flex-shrink: 1なので、場所が足りないと縮みます。 今回は中身のないロゴが0pxまで縮み、それでも足りない分がはみ出しました。 flex-shrink: 0とflex-wrap: wrapを足すと、はみ出しは0pxになり、自然に折り返します。- Gridの
auto列は中身の最小幅より小さくならないため、狭い画面ではFlexboxより大きくはみ出しました。Gridでヘッダーを作るなら、狭い幅用に列の定義をメディアクエリで切り替えるとよいです。
768px以上ではどれも同じ見た目です。要素の数や幅が変わりやすいヘッダーは、Flexbox+flex-wrapが手堅い選択です。
2. カード一覧:最終行の幅で差が出る
5枚のカードを「1枚あたり最小240px」で並べます。
/* Flexbox */
.c-flex { display: flex; flex-wrap: wrap; gap: 16px; }
.c-flex .card { flex: 1 1 240px; }
/* Grid */
.c-grid { display: grid; gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
実測結果(カード5枚の幅)
| 画面幅 | Flexbox | Grid(auto-fit) |
|---|---|---|
| 320px | 1列:全カード270px | 1列:全カード270px |
| 768px | 2列:A〜D 351px、E 718px | 2列:全カード351px |
| 1280px | 4列:A〜D 296px、E 1230px | 4列:全カード296px |
Flexboxでは、折り返した最終行の1枚が残りの幅をすべて使って伸びました。 これは flex-grow: 1 の正しい動きです。タグやボタンのように「行ごとに詰めたい」ならこの動きが便利で、カードのように「全部同じ幅にしたい」ならGridが向いています。
auto-fit と auto-fill の違い
カードが2枚しかない場合も測りました(1280px)。
| 指定 | カードの幅 | 動き |
|---|---|---|
repeat(auto-fit, minmax(240px, 1fr)) |
607px × 2枚 | 空の列をつぶして、ある要素を広げる |
repeat(auto-fill, minmax(240px, 1fr)) |
296px × 2枚 | 空の列も残し、4列分の幅を保つ |
件数が少ないときに横いっぱいに広げたいなら auto-fit、件数に関係なく同じカード幅にしたいなら auto-fill を選びます。
3. subgrid:カードの中の見出しと本文をそろえる
カードの見出しの長さが違うと、本文の開始位置がカードごとにずれます。subgrid を使うと、カードの中の行を親グリッドの行に合わせられます。
.s-grid { display: grid; gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
/* subgridなし:カードごとに独立したグリッド */
.s-grid .card { display: grid; grid-template-rows: auto 1fr auto; gap: 8px; }
/* subgridあり:カードが親の3行分を使い、行の高さを共有する */
.s-sub .card { grid-row: span 3; grid-template-rows: subgrid; }
実測結果(1280px、1行目の3枚)
| 項目 | subgridなし | subgridあり |
|---|---|---|
| 見出しの高さ | 27px / 54px / 27px | 54px / 54px / 54px |
| 本文の開始位置(y) | 558 / 585 / 558(27pxのずれ) | 754 / 754 / 754(ずれ0) |
| ボタンの位置(y) | 638 / 638 / 638 | 834 / 834 / 834 |
768px(2列)でも、subgridなしは本文の開始位置が27pxずれ、subgridありは同じ位置になりました。ボタンは 1fr のおかげでどちらもそろいますが、見出し・本文の境目までそろうのはsubgridだけです。
subgrid は2023年9月に主要ブラウザで使えるようになり、MDNでは「Baseline Widely available」と表示されています。
4. フォーム:ラベル列の幅をGridに任せる
ラベルと入力欄の2列です。ラベルの長さが「氏名」「メールアドレス」「電話」と違います。
/* Flexbox:1行ずつ横並び */
.f-flex .row { display: flex; gap: 8px; margin-bottom: 8px; }
.f-flex input { flex: 1; min-width: 0; }
/* Grid:フォーム全体で2列 */
.f-grid { display: grid; grid-template-columns: max-content 1fr; gap: 8px; }
実測結果(入力欄の左端 x)
| 画面幅 | Flexbox(氏名/メール/電話) | Grid(氏名/メール/電話) |
|---|---|---|
| 320px | 65 / 145 / 65 | 145 / 145 / 145 |
| 768px | 65 / 145 / 65 | 145 / 145 / 145 |
| 1280px | 65 / 145 / 65 | 145 / 145 / 145 |
Flexboxは行ごとに独立しているので、ラベルが長い行だけ入力欄が右にずれます(80px)。 Flexboxでそろえるにはラベルに固定幅を付ける必要がありますが、Gridは max-content で一番長いラベルに列幅を自動で合わせるので、文言が変わっても崩れません。
5. 聖杯レイアウト:どちらでも作れるが、HTMLの形が変わる
ヘッダー/ナビ・メイン・サイド/フッターの定番レイアウトです。768px以上で3カラム、それ未満で縦積みにします。
/* Grid:HTMLはフラットなまま */
.hg-grid { display: grid; gap: 8px;
grid-template-areas: "hd" "nv" "mn" "as" "ft"; }
@media (min-width: 768px) {
.hg-grid {
grid-template-columns: 160px minmax(0, 1fr) 200px;
grid-template-areas: "hd hd hd" "nv mn as" "ft ft ft";
}
}
.hg-grid > header { grid-area: hd; } /* nav, main, aside, footer も同様 */
/* Flexbox:中段をまとめる div.mid が必要 */
.hg-flex { display: flex; flex-direction: column; gap: 8px; }
.hg-flex .mid { display: flex; flex-direction: column; gap: 8px; }
@media (min-width: 768px) {
.hg-flex .mid { flex-direction: row; }
.hg-flex .mid > nav { flex: 0 0 160px; }
.hg-flex .mid > main { flex: 1; min-width: 0; }
.hg-flex .mid > aside { flex: 0 0 200px; }
}
実測結果(nav/main/aside の幅)
| 画面幅 | Grid | Flexbox |
|---|---|---|
| 320px | 縦積み:すべて270px | 縦積み:すべて254px(div.midの余白の分だけ狭い) |
| 768px | 160 / 342 / 200 | 160 / 326 / 200 |
| 1280px | 160 / 854 / 200 | 160 / 838 / 200 |
どちらも意図どおりに並びました。mainの幅の差16pxは、Flexbox版で追加した div.mid に付けた左右の余白の分です。つまりFlexboxでは2次元の骨格を作るために入れ子の要素が1つ増え、その要素のスタイルも考える必要があります。 GridはHTMLをフラットに保ったまま、grid-template-areas の文字列を書きかえるだけで配置を変えられるのが強みです。
6. よくある崩れ:長いコードやURLで横にはみ出す
サイド120px+メインの2カラムで、メインに改行のない長い1行(pre 要素、overflow: auto 付き)を入れました。
/* Flexbox */
.mw-flex { display: flex; gap: 8px; }
.mw-flex main { flex: 1; }
.mw-flex.fix main { min-width: 0; } /* 直した形 */
/* Grid */
.mw-grid { display: grid; gap: 8px; grid-template-columns: 120px 1fr; }
.mw-grid.fix { grid-template-columns: 120px minmax(0, 1fr); } /* 直した形 */
実測結果(メインの幅/コンテナからのはみ出し量)
| 画面幅 | Flexbox | Flexbox+min-width: 0 |
Grid 1fr |
Grid minmax(0, 1fr) |
|---|---|---|---|---|
| 320px | 1551px/1409px | 142px/0px | 1551px/1409px | 142px/0px |
| 768px | 1551px/961px | 590px/0px | 1551px/961px | 590px/0px |
| 1280px | 1551px/449px | 1102px/0px | 1551px/449px | 1102px/0px |
FlexboxとGridで同じ量だけはみ出し、同じように直りました。 原因も共通です。
- Flexboxの子は既定で
min-width: autoなので、中身の最小幅より小さくなれません。 - Gridの
1frはminmax(auto, 1fr)と同じ意味なので、やはり中身の最小幅が下限になります。
直し方は「最小幅を0にする」の1点です。 Flexboxなら子に min-width: 0、Gridなら列に minmax(0, 1fr) を指定すると、pre の overflow: auto が働き、コードブロックの中だけがスクロールするようになります。
7. コンテナクエリ:置き場所の幅でレイアウトを変える
同じカード部品を、広いメイン列と狭いサイド列に置きます。メディアクエリは画面幅、コンテナクエリは部品を包む要素の幅で判定します。
.cq { container-type: inline-size; } /* 判定の基準になる要素 */
.media { display: grid; gap: 8px; grid-template-columns: 1fr; }
@container (min-width: 400px) {
.media { grid-template-columns: 120px 1fr; } /* 400px以上なら画像を左に */
}
実測結果(コンテナ幅 → 画像の幅と配置)
| 画面幅 | メイン列に置いたカード | サイド列に置いたカード |
|---|---|---|
| 320px | 270px → 縦積み(画像270px) | 270px → 縦積み(画像270px) |
| 768px | 468px → 横並び(画像120px) | 234px → 縦積み(画像234px) |
| 1280px | 809px → 横並び(画像120px) | 405px → 横並び(画像120px) |
768pxでは、同じ画面幅なのに置き場所によって違うレイアウトになりました。1280pxではサイド列も400pxを超えたので横並びに切り替わっています。部品単位で作るコンポーネント設計とGridの相性がよいことがわかります。サイズのコンテナクエリは、2023年2月に主要ブラウザで使えるようになっています。
gap:どちらでも使える余白の指定
gap(row-gap/column-gap)はGridとFlexboxの両方で使えます。要素の間だけに余白が入るので、margin で最後の要素だけ打ち消すといった調整が不要になります。
| 書き方 | 意味 |
|---|---|
gap: 16px |
行と列の間を16px |
gap: 8px 16px |
行の間8px、列の間16px |
column-gap: 1rem |
列の間だけ |
subgridの子は親の gap を引き継ぎます。子で gap を指定すると上書きできます。
組み合わせ方の定番
実務では「外側の骨格はGrid、部品の中の1行はFlexbox」という組み合わせがいちばん多い形です。
| 階層 | 使うもの | 例 |
|---|---|---|
| ページの骨格 | Grid(grid-template-areas) |
ヘッダー/本文/サイド/フッター |
| 一覧 | Grid(auto-fill + minmax()) |
カード、画像ギャラリー |
| 部品の中の行・列 | Grid(subgrid) |
カードの見出し・本文・ボタン |
| 部品の中の1行 | Flexbox | ボタン群、アイコン+文字、タグ |
| 1つの要素の中央寄せ | どちらでも | display: grid; place-items: center; が短い |
実装前のチェックリスト
- 行と列の両方をそろえる必要があるか(あればGrid)
- 折り返した最終行の幅をどうしたいか(そろえるならGrid)
- 縮んでほしくない要素に
flex-shrink: 0を付けたか - 長いURL・コードが入る列に
min-width: 0またはminmax(0, 1fr)を指定したか - 件数が少ないときの見え方を
auto-fitとauto-fillで確認したか - 部品の置き場所で見た目を変えたいならコンテナクエリを使ったか
- 幅320px前後で横スクロールが出ていないか確認したか
よくある質問
Q. 全部Gridで作ってもよいですか?
A. 作れますが、ナビやボタン群のように中身の幅に合わせて並べたい部分はFlexboxのほうが短く書けます。今回のヘッダーの実測でも、Gridの auto 列は狭い画面で258pxはみ出しました。役割で使い分けるのが近道です。
Q. Flexboxで最終行のカードが伸びるのを止めるには?
A. カードに flex-grow: 0 と固定の flex-basis を指定する方法もありますが、行の右側に余白が残ります。すべて同じ幅で並べたいなら、Gridの repeat(auto-fill, minmax(240px, 1fr)) に切り替えるのがいちばん簡単です。
Q. min-width: 0 はいつ付ければよいですか?
A. Flexboxで伸び縮みする子(flex: 1 など)に、長いテキストやコード、表、画像が入る可能性があるときです。Gridでは 1fr の代わりに minmax(0, 1fr) を使います。
Q. メディアクエリとコンテナクエリはどう使い分けますか? A. ページの骨格(何カラムにするか)はメディアクエリ、部品の見た目(横並びか縦積みか)はコンテナクエリ、と分けると整理しやすくなります。
まとめ
- 1方向に中身の大きさで並べるならFlexbox、行と列を枠からそろえるならGrid
- 折り返した最終行は、Flexboxだと伸び、Gridだと列幅がそろう(1280pxで1230px対296pxを実測)
- ラベルと入力欄はGridの
max-contentで自動的に縦位置がそろう - subgridでカード内の見出し・本文の境目までそろえられる(ずれ27px→0px)
- はみ出しの原因は
min-width: autoと1frの最小幅。min-width: 0とminmax(0, 1fr)で直る - コンテナクエリで、置き場所の幅に合わせて部品のレイアウトを切り替えられる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「FlexboxとGridの使い分け|同じUIを両方で作り3つの画面幅で実測比較」
株式会社RJCでは、レイアウトの判断を実際に測って確かめる習慣を、プロジェクトの中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。