The "Grid vs Flexbox" debate misses the point. They're not competing tools — they solve different problems. Understanding which to reach for in any situation makes your CSS cleaner, more maintainable, and easier to reason about.
Flexbox = one-dimensional layout (a row OR a column) Grid = two-dimensional layout (rows AND columns simultaneously)
If you're arranging items in a single line — a navigation bar, a button group, a card's content area — that's Flexbox. If you're creating a page layout with rows and columns that need to align — a product grid, a dashboard, a magazine layout — that's Grid.
Flexbox shines inside components where you're arranging a single flow of items.
/* Perfect Flexbox use case: items in a row with space between */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
height: 60px;
}
.navbar-links {
display: flex;
align-items: center;
gap: 24px;
list-style: none;
}.card {
display: flex;
flex-direction: column;
}
.card-content {
flex: 1; /* Takes remaining space, pushing footer to bottom */
padding: 16px;
}
.card-footer {
padding: 16px;
border-top: 1px solid #eee;
}This "push footer to bottom" pattern is classic Flexbox. Grid would be cumbersome here.
/* Centering a single element — Flexbox is simplest */
.container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}.button-group {
display: flex;
gap: 8px;
flex-wrap: wrap; /* Wraps on small screens */
}Grid is the right choice when items need to align across both rows and columns simultaneously.
/* Classic page layout that would be painful with Flexbox */
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 60px 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
min-height: 100vh;
}
.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.main { grid-area: main; }
.footer { grid-area: footer; }/* Auto-fill columns — items wrap automatically based on min/max */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}This single rule creates a fully responsive grid — no media queries needed. 3 columns on desktop, 2 on tablet, 1 on mobile. This is impossible to replicate cleanly with Flexbox.
.dashboard {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}
/* Wide card spans 8 columns, narrow spans 4 */
.stats-overview { grid-column: span 8; }
.recent-activity { grid-column: span 4; }
/* Full-width chart below */
.main-chart { grid-column: 1 / -1; }
@media (max-width: 768px) {
.stats-overview,
.recent-activity,
.main-chart { grid-column: 1 / -1; }
}body {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
header { grid-column: 1 / -1; }
.left-sidebar { grid-column: 1; }
main { grid-column: 2; }
.right-sidebar { grid-column: 3; }
footer { grid-column: 1 / -1; }The best layouts often use both. Grid for the macro layout, Flexbox for component internals:
/* Grid handles the page structure */
.app {
display: grid;
grid-template-columns: 280px 1fr;
grid-template-rows: 64px 1fr;
}
/* Flexbox handles the header's internal arrangement */
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}
/* Grid handles the card grid */
.post-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
padding: 24px;
}
/* Flexbox handles each card's internal layout */
.post-card {
display: flex;
flex-direction: column;
}
.post-card-content {
flex: 1;
}1. Using Flexbox to create a card grid. The famous flex-wrap card grid where the last row has misaligned items of different widths:
/* BAD: Last row looks wrong if fewer than 3 items */
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 300px; /* Items in last row stretch to fill */
}
/* GOOD: Grid handles this correctly */
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
}2. Using Grid for a simple button row.
/* Overkill */
.actions {
display: grid;
grid-auto-flow: column;
gap: 8px;
}
/* Right tool */
.actions {
display: flex;
gap: 8px;
}3. Forgetting gap exists. Still seeing margin-right on all-but-last-child. Use gap — it works in both Flexbox and Grid and doesn't add margin to the last item.
4. Not using fr units. The fractional unit in Grid is one of its killer features:
/* Three equal columns */
grid-template-columns: 1fr 1fr 1fr;
/* Sidebar + main content with 3:1 ratio */
grid-template-columns: 1fr 3fr;| Use Flexbox when... | Use Grid when... |
|---|---|
| Content in a single row or column | Layout needs rows AND columns |
| Items have variable/unknown size | Items need to align across rows |
| Navigation, button groups, form rows | Page layout, card grids, dashboards |
| Content dictates layout | Layout dictates content placement |
repeat(auto-fill, minmax(Xpx, 1fr)) is the best responsive grid pattern — no media queries neededgap works in both Flexbox and Grid — stop using margin hacks for spacing between items