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 →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.
Run Two Claude Code Accounts at Once (Personal + Office)
Claude Code has no account switcher yet, but one environment variable lets you keep a personal and an office login active at the same time. Here's the full setup.
React Server Components in Depth — What They Are and When to Use Them
React Server Components fundamentally change how we think about rendering. This guide breaks down how they work, how they differ from Client Components, and the patterns that will make your Next.js apps faster.