A practical guide to CSS Grid vs Flexbox

7 min read
css
frontend

The question "Grid or Flexbox?" comes up in almost every CSS layout decision, and the honest answer is that they were designed for different axes of a problem — most real interfaces end up using both.

The core distinction

Flexbox is **one-dimensional**: it lays items out along a single axis (row or column) and is built to distribute space and handle wrapping along that one axis. Grid is **two-dimensional**: it lets you define rows and columns simultaneously and place items anywhere in that grid, independent of source order.

If you're arranging things in a line — a navbar, a button group, a list of tags — that's Flexbox's job. If you're arranging things in a grid — a card layout, a page template with header/sidebar/content/footer — that's Grid's job.

Flexbox in practice

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

Flexbox shines when the number of items is unknown and content should determine sizing — a wrapping tag list, a toolbar, a set of form controls that need to stay aligned along one line. `justify-content` controls spacing along the main axis, `align-items` controls alignment along the cross axis, and `flex-wrap` lets items fall onto new lines when they run out of room, at which point each new line becomes its own independent flex line rather than part of a shared grid.

Grid in practice

.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "sidebar header"
    "sidebar content"
    "sidebar footer";
  min-height: 100vh;
}

Grid shines whenever rows and columns both matter — a page template, a photo gallery with consistent cells, a dashboard of cards that need to align both horizontally and vertically. `grid-template-areas` gives you a literal ASCII-art layout that's easy to read and rearrange for responsive breakpoints, and `fr` units let you distribute remaining space proportionally without doing width math.

The common pattern: Grid for structure, Flexbox for content

A typical card component uses Grid for the overall page layout and Flexbox inside each card to arrange its internal content:

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; }
.card { display: flex; flex-direction: column; gap: 0.5rem; }
.card__footer { display: flex; justify-content: space-between; margin-top: auto; }

`repeat(auto-fill, minmax(220px, 1fr))` is one of Grid's most useful patterns: it creates as many equal-width columns of at least 220px as fit, with no media query needed for the common "responsive card grid" case.

When either would technically work

For a simple row of equally-sized items, both can do it, and the deciding factor becomes what you need next: if you'll want to control both row and column alignment independently, or place an item out of source order, reach for Grid. If it's genuinely a single line (or a single axis that wraps), Flexbox's API is simpler for that case and avoids defining a grid you don't need.

A quick decision checklist

  • Two dimensions matter (rows *and* columns) → Grid.
  • One dimension, content-driven sizing → Flexbox.
  • Need items placed out of DOM order → Grid (`grid-column`/`grid-row`, or named areas).
  • Need to center or space out items along one line → Flexbox.
  • Building a page-level template → Grid, with Flexbox for the components inside it.

Try both interactively with the [Flexbox builder](/tools/html-css/flexbox-builder) and the [Grid builder](/tools/html-css/grid-builder) — toggling individual properties and watching the live preview update is the fastest way to build intuition for how each axis behaves.

Tools from this article

← All articles