ESC

Type to search the knowledge base.

CSS Grid vs Flexbox When to Use

One-dimensional flex vs two-dimensional grid — decision rules for navs, card grids, page shells, and nested layouts.

beginner3 min read
  • css
  • grid
  • flexbox
  • layout

Flexbox distributes space along one axis (row or column). Grid aligns content on rows and columns together. You will use both in the same UI: grid for the page skeleton and card matrix, flex for toolbars and chip rows inside cells.

Docs: MDN Grid vs Flexbox, Flexbox, Grid.

Decision rule

Question Lean
Am I lining up items in a single row or column? Flex
Do I need rows and columns aligned as a system? Grid
Content should dictate track size fluidly along one line? Flex
I want explicit template areas / 12-column structure? Grid
Toolbar: logo left, actions right? Flex
Page: header / main / sidebar / footer? Grid (or grid + flex inside)

Flex wins

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.toolbar__actions {
  margin-inline-start: auto;
  display: flex;
  gap: 0.5rem;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

Nav links, form field + button rows, card footers with a primary action pushed end — all classic flex.

Grid wins

.page {
  display: grid;
  grid-template-columns: 16rem 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "nav main"
    "footer footer";
  min-height: 100dvh;
}

.page > header { grid-area: header; }
.page > nav { grid-area: nav; }
.page > main { grid-area: main; min-width: 0; }
.page > footer { grid-area: footer; }

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
}

Two-dimensional alignment (same column widths across rows, named areas, dense photo masonry with grid-auto-flow: dense) is grid territory.

Nested reality

<section class="product-grid">
  <article class="card">
    <div class="card__body">…</div>
    <footer class="card__footer">
      <span class="price">$12</span>
      <button type="button">Add</button>
    </footer>
  </article>
</section>
.card {
  display: flex;
  flex-direction: column;
}

.card__footer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-block-start: auto;
}

.card__footer .price {
  margin-inline-end: auto;
}

Outer grid for matrix; inner flex for component chrome. Forcing everything into one model produces awkward gaps and magic numbers.

Overlap cases

  • Equal-height cards in a row — both work; grid rows align by default; flex items stretch on the cross axis.
  • Holy grail layout — grid areas are clearer than nested flex.
  • Input + button — flex; don’t open grid for two siblings unless alignment to a larger matrix matters.

Interview out-loud

“Flex is one-dimensional: main axis distribution and cross-axis alignment. Grid is two-dimensional: rows and columns as one system. I use flex for toolbars, nav, and in-component packing; grid for page shells and card matrices. Nesting both is normal. gap works in both and beats margin hacks.”

Footguns

  1. Building a full page only with nested flex when grid areas are simpler.
  2. Using grid for a single row of buttons — flex is enough.
  3. Forgetting min-width: 0 / min-height: 0 on nested flex/grid children that must scroll.
  4. Mixing float leftovers with modern layout.
  5. Media-query column counts when auto-fit + minmax would suffice.

Quick refactor heuristic

If you are counting columns with media queries and every child should share row alignment, switch the outer layout to Grid. If you are fighting align-items on a single row of actions, stay on Flex. When in doubt for components, Flex; for pages and matrices, Grid. Revisit when a “flex wrap card row” grows explicit row/column alignment needs — that is the usual migration trigger in code review.

Further reading

Related guides