Two layout engines now do what tables, floats, and inline-block hacks took twenty years to approximate. But "Flexbox or Grid?" produces its own kind of paralysis. The mental model separates cleanly: flexbox aligns items along one axis; grid defines a two-dimensional track system. Most pages need both.
The decision rule in one line#
Use flexbox when the content determines the layout (a row of items that should flow). Use grid when the layout should determine where content goes (a page scaffold with named regions).
Flexbox is content-first: it sizes and aligns whatever items you hand it, in one direction. Grid is container-first: you draw columns and rows up front, then drop items into cells.
Flexbox: one axis, alignment superpowers#
Flexbox shines wherever items need to fit a line and align — buttons in a toolbar, navigation links, avatar + text in a list row.
.nav {
display: flex;
gap: 1rem;
align-items: center; /* cross axis */
justify-content: space-between; /* main axis */
}
Three properties carry most real work:
justify-content— distribution along the main axis (left-to-right or row)align-items— alignment along the cross axis (vertical centering)gap— spacing between items (no margin juggling)
.sidebar { display: flex; flex-direction: column; justify-content: center; }— vertically centered column.
Useful gotcha: flex-shrink defaults to 1, so a long label can squeeze a sibling. Set flex-shrink: 0 (or min-width) on elements that must keep their size.
Grid: the two-dimensional scaffold#
Grid is the right tool when the page itself has regions: header, sidebar, main, footer. You define the skeleton once in the container, then place content without fighting the source order.
.app {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.sidebar { grid-area: sidebar; }
Capabilities that flexbox simply does not have:
- True two-dimensional placement (
grid-column: 1 / 3spans two tracks) - Named areas that read like a blueprint
frunits that flexibly divide leftover space alongside fixed tracks- Precise alignment of items to each other in both axes at once
A dashboard with a chart next to a list, where both must share height, is a grid job:
.dashboard { display: grid; grid-template-columns: 2fr 1fr; }.
When people reach for the wrong one#
| Goal | Right engine |
|---|---|
| Center a button inside a card | Flexbox (justify-content/align-items) |
| Horizontal nav that wraps on mobile | Flexbox (flex-wrap) |
| Whole-page skeleton (sidebar + main) | Grid |
| A gallery of equal cards | Grid (repeat(auto-fill, minmax(240px, 1fr))) |
| A row where one item stretches to fill | Flexbox (flex: 1) |
The recurring complaint "flexbox can't make cards equal-height in two lines" is really "don't use two flex rows"; wrap them in one grid.
Two recipes that cover most layouts#
Centered card:
.card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
Responsive gallery without media queries:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1rem;
}
auto-fill + minmax(240px, 1fr) gives you fluid columns that collapse gracefully at every width — one of the best arguments for grid over flexbox in list-heavy pages.
Sticky footer: a classic both can solve#
The "footer pinned to the bottom when content is short" problem is a layout decision, not an alignment one, so grid handles it most compactly:
body {
display: grid;
grid-template-rows: 1fr auto;
min-height: 100dvh;
}
Content takes 1fr of whatever space remains; the footer only takes its natural height.
Performance note#
Layout cost is proportional to how many elements re-layout on change. A subtle but real difference here: flexbox reflows the whole line when one item changes size; grid reflows only affected tracks. For animation-heavy UI, push every row into track layout when possible. This ties into the deeper rendering story in React's rendering model explained.
Check your mental model#
- Am I aligning items along one axis? → Flexbox.
- Am I defining a scaffold with columns and rows? → Grid.
- Do I need equal-height siblings or column spans? → Grid.
- Is this a row of variable content that must hug its content? → Flexbox.
Neither replaces the other — pages you ship will contain both. Consider also how the container keeps its structure across breakpoints; the core web vitals guide shows why layout stability is worth measuring as you add these patterns.