Every few months this debate resurfaces. The answer hasn't changed: use both, for different things.
Grid and Flexbox are complementary tools. Knowing when to reach for each is a fundamental CSS skill.
The One-Line Rule
- Flexbox = one-dimensional layout (a row or a column)
- Grid = two-dimensional layout (rows and columns simultaneously)
Everything flows from this.
Flexbox: What It's Great At
Navigation bars
.nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
A nav is a single row of items with space between them. That's Flexbox's home turf.
Centering a single element
.container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
The most common use case in all of CSS. Grid works too, but Flexbox is more expressive here.
Card content layout
Inside a card, you want the title and body to grow, and the button to pin to the bottom:
.card {
display: flex;
flex-direction: column;
}
.card-body {
flex: 1; /* grows to fill available space */
}
.card-button {
margin-top: auto; /* pins to bottom */
}
[!TIP]
margin-top: autoin a flex column is the cleanest way to push an element to the bottom of its container. No absolute positioning needed.
Grid: What It's Great At
Page-level layouts
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
min-height: 100vh;
}
This creates a complete app layout (sidebar, header, main content, footer) in 8 lines. Try doing that with Flexbox.
Card grids that adapt to content
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
auto-fill + minmax = a responsive grid with no media queries. Cards reflow automatically as the viewport shrinks.
Overlapping elements
Grid allows you to place multiple items in the same cell:
.hero {
display: grid;
}
.hero-image,
.hero-text {
grid-column: 1;
grid-row: 1;
}
Both elements occupy the same space. The text overlays the image. This is awkward with Flexbox and impossible without position: absolute.
The "Which One?" Decision Tree
Does the layout have both rows AND columns?
├── YES → Grid
└── NO (just one axis)
├── Is it a single item to be centered?
│ └── Either works, Flexbox is simpler
├── Do children need to wrap to new lines?
│ ├── YES and they need to align across rows? → Grid
│ └── YES but rows are independent? → Flexbox with wrap
└── Is it a simple row/column of items? → Flexbox
Common Mistakes
Mistake 1: Using Flexbox for a card grid
/* ❌ Don't do this */
.cards {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.card {
flex: 0 0 calc(33.333% - 1rem);
}
This breaks at different viewport sizes and requires math to handle gaps. Use Grid instead:
/* ✅ Do this */
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
Mistake 2: Using Grid for simple alignment
/* ❌ Overkill */
.header {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
}
/* ✅ Clear intent */
.header {
display: flex;
align-items: center;
gap: 1rem;
}
Mistake 3: Forgetting you can nest them
The real power is combining both:
/* Outer structure: Grid */
.layout {
display: grid;
grid-template-columns: 240px 1fr;
}
/* Inner content: Flexbox */
.sidebar {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
Use Grid for the page-level skeleton. Use Flexbox inside each section.
Modern CSS That Changes Things
In 2026, container queries change the equation slightly:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
@container (min-width: 600px) {
.card {
grid-template-columns: auto 1fr;
}
}
But the fundamental rule still holds: Grid for 2D structure, Flexbox for 1D alignment.
Quick Reference
| Use Case | Winner |
|---|---|
| Navbar with logo + links + button | Flexbox |
| Responsive card grid | Grid |
| Centering a modal | Either (Flexbox is simpler) |
| Full-page app layout | Grid |
| Card with pinned footer button | Flexbox |
| Overlapping hero text on image | Grid |
| Form with label + input pairs | Grid |
| Tag/badge list that wraps | Flexbox |
| Dashboard with sidebar + main | Grid |
| Button with icon + text | Flexbox |
Tagged with
Written by
DebuggerMe TeamThe DebuggerMe team builds developer tools, writes technical content, and helps teams ship better software.
Related Articles
All articles →Safari 19 Introduces CSS Anchor Positioning
Apple's Safari 19 introduces native CSS Anchor Positioning, eliminating the need for absolute coordinate JavaScript library fallbacks.
React 19 Stable Is Finally Here
React 19 has entered stable release, introducing the React Compiler, server actions, and cleaner async form handling hooks.
Stop Writing API Wrappers. Use TanStack Query Instead
Most frontend codebases have a homegrown API layer full of useEffect hacks, loading booleans, and stale data bugs. TanStack Query solves all of these in 20 lines. Here's how to migrate.