# CSS Grid vs Flexbox: How to Choose for Each Layout

Source: https://devdocs10.com/articles/css/css-grid-vs-flexbox  
By Murat Bo · Published 2026-10-17 · DevDocs10 (https://devdocs10.com)

Grid and Flexbox overlap enough that either can produce most simple layouts, which makes the choice feel arbitrary. It is not: they size items in opposite directions, and that difference decides how a layout behaves when the content, the viewport, or the number of items changes. This guide gives you a default, the criteria behind it, and live examples you can edit.

> **Quick answer:** Use Grid when the container defines the layout: you know the columns or rows, and items must line up across them (page shells, card galleries, forms, dashboards). Use Flexbox when the content defines the layout: items of varying size flow along one line and share the leftover space (toolbars, navigation, tag lists, button groups). Most real interfaces use Grid for the structure and Flexbox inside the pieces.

> **Difficulty:** Intermediate
>
> **You'll understand this better if you know:** [Flexbox basics](/articles/css/css-flexbox-basics), [Grid basics](/articles/css/css-grid-basics) (tracks, `fr`, `gap`), and the box model.

## 1. The Real Difference: Who Sizes the Items?

"Flexbox is one-dimensional, Grid is two-dimensional" is true, but it is a consequence. The underlying difference is where sizes come from.

- **Flexbox works from the content out.** Each item starts from a flex base size, taken from `flex-basis` (with `auto`, its `width`, or its content size if the width is also `auto`). The container then hands out the remaining space, or takes back the overflow, using `flex-grow` and `flex-shrink`. When items wrap, each line repeats this on its own, so items on the second line know nothing about the first.
- **Grid works from the container in.** The container defines tracks, and items are placed into the areas between grid lines. Tracks can be fixed, flexible (`fr`), or content-based (`auto`, `min-content`, `max-content`, `fit-content()`). A content-based track is sized from *every* item in it, so items in the same column share a width and items in the same row share a height.

That is why Grid aligns things in two directions: tracks are shared. Flex lines are independent, so nothing lines up across them unless you force it with fixed sizes. MDN's [Relationship of grid layout to other layout methods](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Relationship_of_grid_layout_with_other_layout_methods) describes the same split as "layout in" versus "content out".

## 2. Decision Criteria at a Glance

| Criterion | Flexbox | Grid |
| --- | --- | --- |
| Sizing source | Content first; free space shared by `flex-grow`/`flex-shrink` | Track definitions first; content-based tracks consider every item in the track |
| Dimensions | One axis at a time | Rows and columns together |
| Alignment across wrapped lines | None: each line sizes and aligns itself | Built in: items in a column share its width |
| Wrapping | `flex-wrap`; a short last line can stretch | `repeat(auto-fill, minmax())`; wrapped items keep the column width |
| Items with different natural widths in one row | Natural fit | Forced into tracks |
| Explicit placement, spanning, overlap | No (only `order`) | `grid-column`, `grid-row`, named areas, overlapping items |
| Aligning nested content across siblings | No | Yes, with `subgrid` |
| Pushing an item to the end | `margin-inline-start: auto` | An `auto` margin, or a `1fr` track before it |
| Responsive without media queries | Breakpoints emerge from `flex-basis` and content | Column count emerges from `minmax()` track sizes |
| Default minimum size of items | `min-width: auto` (content-based) | `min-width: auto`, and `1fr` means `minmax(auto, 1fr)` |
| Visual reordering | `order`, `*-reverse`; does not change reading or focus order | Placement, `order`, `dense` packing; same caveat |

## 3. The Trade-offs in Detail

### Content-driven vs layout-driven sizing

The `flex` shorthand decides how much content influences the result, and the common values behave very differently (see the [Flexbox specification](https://www.w3.org/TR/css-flexbox-1/#flex-common)):

- `flex: 1` means `1 1 0`: every item starts from zero and receives an equal share, so widths ignore content (until an item's minimum content width stops it from shrinking).
- `flex: auto` means `1 1 auto`: items start from their content size and share the leftover, so wider content stays wider.
- `flex: none` means `0 0 auto`: the item keeps its `width` (or its content size if the width is `auto`) and neither grows nor shrinks.

Grid is not limited to fixed sizes. `auto`, `max-content`, and `fit-content()` tracks size to content too; the difference is that the decision is made once for the whole track rather than per item. The form example in section 4 relies on exactly that.

### Wrapping: independent lines vs shared columns

This is the difference people hit first. Both containers below hold seven cards with the same 140px minimum. Open the preview, then edit the widths or the number of cards:

```html
<style>
  .label { margin: 0 0 6px; font: 600 13px/1.4 system-ui, sans-serif; }
  .demo { max-width: 520px; margin-bottom: 16px; gap: 8px; }
  .card { box-sizing: border-box; padding: 8px 12px; border: 1px solid #93c5fd; border-radius: 6px; background: #dbeafe; font: 14px system-ui, sans-serif; }

  .flex-cards { display: flex; flex-wrap: wrap; }
  .flex-cards .card { flex: 1 1 140px; }

  .grid-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .grid-cards .card { border-color: #86efac; background: #dcfce7; }
</style>

<p class="label">Flexbox: flex: 1 1 140px with flex-wrap</p>
<div class="demo flex-cards">
  <div class="card">1</div><div class="card">2</div><div class="card">3</div>
  <div class="card">4</div><div class="card">5</div><div class="card">6</div>
  <div class="card">7</div>
</div>

<p class="label">Grid: repeat(auto-fill, minmax(140px, 1fr))</p>
<div class="demo grid-cards">
  <div class="card">1</div><div class="card">2</div><div class="card">3</div>
  <div class="card">4</div><div class="card">5</div><div class="card">6</div>
  <div class="card">7</div>
</div>
```

With Flexbox, the seventh card sits alone on the last line, and because each line distributes its own free space, it grows to the full width. With Grid, the column widths come from the container, so the seventh card stays in the first column at the same width as the others. If the stretched last card is what you want (for example, buttons that should fill a row), Flexbox is right. If wrapped items should form columns, use Grid.

> **Note:** With fewer items than fit in one row, `auto-fill` keeps the empty tracks, while `auto-fit` collapses them so the existing items stretch across the row.

### Minimum sizes and overflow

Both models share a default that causes many "my layout overflows" bugs: flex items and grid items have `min-width: auto`, which resolves to their content-based minimum. A long URL or a wide code block then forces the item wider than its share. In Grid, a bare `1fr` track has an `auto` minimum for the same reason.

```css
/* Flex and grid items default to min-width: auto, so a long URL or a wide
   <pre> can force an item wider than its share and overflow the page. */

.layout-flex > main {
  flex: 1;
  min-width: 0;                                 /* allow the item to shrink below its content */
}

.layout-grid {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);  /* a bare 1fr means minmax(auto, 1fr) */
}

main pre {
  overflow-x: auto;                             /* the long line now scrolls inside its box */
}
```

Setting `overflow` to a scrolling value on the item itself also removes the automatic minimum, because both specifications exempt scroll containers.

### Source order and accessibility

Both models can show items in a different order from the HTML: `order` and the `*-reverse` directions in Flexbox, and line placement, named areas, `order`, and `grid-auto-flow: dense` in Grid. Screen readers and keyboard focus still follow the DOM. The Grid and Flexbox specifications both say to use these features "only for visual, not logical, reordering of content" ([CSS Grid Level 2](https://www.w3.org/TR/css-grid-2/#order-accessibility)). Grid's placement power makes this easier to get wrong, so if the visual order differs from the reading order, change the HTML.

### Browser behavior and support

Flexbox and Grid are supported in every current browser. Two newer pieces are worth checking against your support matrix: `gap` in flex containers (Chrome 84, Firefox 63, Safari 14.1) and [subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid) (Chrome and Edge 117, Firefox 71, Safari 16). Performance is not a reason to pick one: both are native layout algorithms, and for normal interfaces you should choose on behavior and only investigate if the Performance panel shows long layout work.

## 4. Realistic Scenarios

### Scenario 1: A site header or toolbar → Flexbox

A logo, a search field, a few links, and an account menu. The number of items changes between pages and their widths come from their text.

```css
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}

.toolbar .search {
  flex: 1 1 12rem;              /* takes the leftover space; wraps below 12rem */
}

.toolbar .account {
  margin-inline-start: auto;    /* pushed to the far end of its line */
}
```

**Why Flexbox:** every item keeps its natural width, the search field absorbs the leftover space, the account menu is pushed to the end with an auto margin, and the whole bar wraps gracefully. Grid would require you to decide the columns up front and update them whenever an item is added.

### Scenario 2: A product or card gallery → Grid

An unknown number of cards that should form tidy columns at any viewport width.

**Why Grid:** `repeat(auto-fill, minmax(14rem, 1fr))` picks the column count from the available width without media queries, every card in a column has the same width, and the last row does not stretch, as the wrapping example above showed. Add `subgrid` when titles, prices, and buttons inside the cards must line up too.

### Scenario 3: A form with aligned labels → Grid

Labels of different lengths next to inputs. The label column should be as wide as the longest label, and every input should start at the same position.

```html
<style>
  .label { margin: 0 0 6px; font: 600 13px/1.4 system-ui, sans-serif; }
  .form { max-width: 520px; margin: 0 0 16px; font: 14px system-ui, sans-serif; }
  .form input { padding: 6px 8px; border: 1px solid #9ca3af; border-radius: 4px; font: inherit; }

  /* Flexbox: every row is its own flex container */
  .flex-row { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
  .flex-row input { flex: 1; min-width: 0; }

  /* Grid: one container, one shared label column */
  .form-grid { display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: center; gap: 8px 12px; }
</style>

<p class="label">Flexbox: each row sizes its own label</p>
<form class="form">
  <div class="flex-row"><label for="f-name">Name</label><input id="f-name"></div>
  <div class="flex-row"><label for="f-email">Email address</label><input id="f-email"></div>
  <div class="flex-row"><label for="f-phone">Phone</label><input id="f-phone"></div>
</form>

<p class="label">Grid: the label column fits the longest label</p>
<form class="form form-grid">
  <label for="g-name">Name</label><input id="g-name">
  <label for="g-email">Email address</label><input id="g-email">
  <label for="g-phone">Phone</label><input id="g-phone">
</form>
```

**Why Grid:** this is a content-driven layout that still needs alignment across rows. Each flex row sizes its own label, so the inputs start at different positions. One grid with a `max-content` label column sizes that column from all labels at once. The usual Flexbox fix, a fixed label width, breaks as soon as a translation or a longer label appears.

### Scenario 4: Tags, chips, and button groups → Flexbox

A list of tags with different text lengths that wraps onto as many lines as needed.

```css
/* Flexbox: every tag is as wide as its text */
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

/* Grid would force every tag into an equal-width column:
   .tags { display: grid; grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); } */
```

**Why Flexbox:** the tags should be exactly as wide as their text, and nothing needs to align across lines. A grid would put each tag into an equal-width column, wasting space around short tags and squeezing long ones.

## 5. Decision Checklist

Go through the questions in order and stop at the first "yes":

1. **Must items line up with each other in both directions,** such as columns that stay aligned across rows? Use Grid.
2. **Do you need to place items in specific cells, span several tracks, or overlap items?** Use Grid.
3. **Must content inside sibling components line up,** such as card titles and footers? Use Grid with `subgrid`.
4. **Is it a single row or column whose items should keep their natural size and share the leftover space?** Use Flexbox.
5. **Do items wrap?** If wrapped items should form consistent columns, use Grid with `auto-fill` or `auto-fit` and `minmax()`. If each line may size itself, use Flexbox with `flex-wrap`.
6. **Is the number of items unknown and their size driven by their text,** such as navigation links or actions? Use Flexbox.
7. **Still unsure?** Use Grid for the outer structure of the page or component and Flexbox for the items inside each region.

## 6. Wrong Reasons to Pick One

- **"Grid is for pages, Flexbox is for components."** Size is not the criterion. A three-field form is a small component and a Grid problem; a site header spans the page and is a Flexbox problem.
- **"Grid is newer, so it replaces Flexbox."** They are complementary specifications that solve different problems, and both are current.
- **"I need wrapping, so I need Flexbox."** Grid wraps too, with `auto-fill` and `auto-fit`. The question is whether wrapped items should align.
- **"Grid needs fixed sizes."** `auto`, `max-content`, and `fit-content()` tracks are content-based.
- **"Only Flexbox can center things."** `display: grid; place-items: center;` centers a child in both directions as well.
- **"I'll use `order` to rearrange things for mobile."** That changes the visual order only; keyboard and screen reader users get the DOM order. Reorder the HTML instead.
- **"One of them is faster."** Not in a way that should drive the decision. Choose on behavior.

## 7. When to Mix Both

Mixing is the normal case, not a compromise. Any grid item can be a flex container and any flex item can be a grid container. The pattern that holds up best is Grid for regions and repeated structures, and Flexbox for the row or column of items inside each region:

```html
<style>
  .app {
    display: grid;
    grid-template-columns: 120px minmax(0, 1fr);
    grid-template-areas:
      "header header"
      "nav    main";
    gap: 8px;
    font: 14px/1.4 system-ui, sans-serif;
  }

  /* Flexbox inside the header: content-sized items, actions pushed to the end */
  .app-header {
    grid-area: header;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 6px;
    background: #1e3a8a;
    color: #fff;
  }
  .app-header .actions { display: flex; gap: 6px; margin-inline-start: auto; }
  .app-header button { padding: 4px 10px; border: 0; border-radius: 4px; font: inherit; }

  .app-nav { grid-area: nav; display: flex; flex-direction: column; gap: 4px; }
  .app-nav a { padding: 4px 8px; border-radius: 4px; background: #e0e7ff; color: #1e3a8a; text-decoration: none; }

  /* Grid for the tiles; subgrid lines up titles, text, and footers across tiles */
  .app-main { grid-area: main; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
  .tile {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    gap: 4px;
    padding: 10px;
    border: 1px solid #cbd5e1;
    border-radius: 6px;
  }
  .tile h3 { margin: 0; font-size: 15px; }
  .tile p { margin: 0; color: #374151; }
  .tile footer { display: flex; gap: 6px; }
  .tile footer span { padding: 2px 6px; border-radius: 4px; background: #f1f5f9; font-size: 12px; }

  @media (max-width: 480px) {
    .app { grid-template-columns: minmax(0, 1fr); grid-template-areas: "header" "nav" "main"; }
    .app-nav { flex-direction: row; flex-wrap: wrap; }
  }
</style>

<div class="app">
  <header class="app-header">
    <strong>Acme</strong>
    <span>Projects</span>
    <div class="actions"><button type="button">Share</button><button type="button">New</button></div>
  </header>
  <nav class="app-nav">
    <a href="#">Overview</a><a href="#">Tasks</a><a href="#">Settings</a>
  </nav>
  <main class="app-main">
    <article class="tile">
      <h3>Launch plan</h3>
      <p>Short summary.</p>
      <footer><span>Due Fri</span></footer>
    </article>
    <article class="tile">
      <h3>Customer research interviews</h3>
      <p>A longer description that wraps onto several lines in a narrow tile.</p>
      <footer><span>12 notes</span><span>3 owners</span></footer>
    </article>
    <article class="tile">
      <h3>Docs</h3>
      <p>Medium length text here.</p>
      <footer><span>Draft</span></footer>
    </article>
  </main>
</div>
```

In this example:

- **Grid** defines the shell with named areas, and a media query switches it to a single column on narrow screens without touching the HTML.
- **Flexbox** lays out the header, where items are sized by their text and the actions are pushed to the end with `margin-inline-start: auto`, and the navigation, which changes from a column to a wrapping row.
- **Grid** arranges the tiles, and `grid-template-rows: subgrid` lets each tile reuse three rows of the parent grid, so titles, descriptions, and footers line up across tiles even though the text lengths differ.
- **Flexbox** returns inside each tile footer for the small badges.

## 8. Key Points

- Flexbox sizes from the content out; Grid sizes from the container in. "One- vs two-dimensional" follows from that.
- Use Grid when items must align across rows and columns, when you need placement or spanning, and when nested content must line up (`subgrid`).
- Use Flexbox for a single line of items with natural sizes that share leftover space: toolbars, navigation, tags, button groups.
- Wrapped flex lines are independent, so a short last line stretches; Grid keeps wrapped items in columns.
- Grid can be content-driven too, with `auto`, `max-content`, and `fit-content()` tracks.
- Both default to `min-width: auto` for items; use `min-width: 0` or `minmax(0, 1fr)` when long content overflows.
- Visual reordering in either model does not change reading or focus order.
- Grid for structure and Flexbox inside each region is the default combination.

## 9. Final Summary

Choosing between Grid and Flexbox is choosing who decides the sizes. When the container should decide, because items must form columns, align across rows, span areas, or share internal structure through subgrid, use Grid. When the items should decide, because each one is as wide as its content and the line simply distributes what is left, use Flexbox.

The checklist above resolves most cases in a few questions, and the cases it does not resolve are usually layouts that need both: Grid for the regions and repeated structures, Flexbox for the items inside them. Keep the HTML in reading order, watch for the `min-width: auto` overflow trap, and let the content-versus-layout question make the call rather than habit.
