CSS Flexbox vs Grid: When to Use Which
· 12 min read
Table of Contents
Choosing between CSS Flexbox and Grid can feel overwhelming, especially when both seem capable of solving the same layout problems. The truth is, they're designed for different purposes, and understanding their strengths will make you a more effective developer.
This guide breaks down everything you need to know about Flexbox and Grid, from core concepts to real-world applications. By the end, you'll know exactly which tool to reach for in any layout scenario.
Flexbox Basics (1D Layout)
Flexbox is designed for one-dimensional layouts — arranging items in a single row or column. It excels at distributing space and aligning items along one axis, making it perfect for navigation bars, button groups, and content that flows in a single direction.
Core Concepts
Every Flexbox layout has a flex container (parent) and flex items (children). The container controls the direction, wrapping, and alignment of its items:
/* The flex container */
.container {
display: flex;
flex-direction: row; /* row | row-reverse | column | column-reverse */
justify-content: center; /* Main axis alignment */
align-items: center; /* Cross axis alignment */
gap: 1rem; /* Space between items */
flex-wrap: wrap; /* Allow items to wrap to next line */
}
/* Flex items */
.item {
flex: 1; /* Grow to fill available space */
/* Shorthand for: flex-grow: 1; flex-shrink: 1; flex-basis: 0; */
}
.item-fixed {
flex: 0 0 200px; /* Don't grow, don't shrink, 200px wide */
}
Understanding the Main and Cross Axes
The key to mastering Flexbox is understanding its two axes. The main axis runs in the direction set by flex-direction (horizontal for row, vertical for column). The cross axis runs perpendicular to it.
This axis system determines which properties control which dimensions. justify-content always works on the main axis, while align-items works on the cross axis.
Alignment Properties
/* Main axis (horizontal in row direction) */
.container {
justify-content: flex-start; /* Default: pack items to start */
justify-content: flex-end; /* Pack items to end */
justify-content: center; /* Center items */
justify-content: space-between;/* Equal space between items */
justify-content: space-around; /* Equal space around items */
justify-content: space-evenly; /* Equal space everywhere */
}
/* Cross axis (vertical in row direction) */
.container {
align-items: stretch; /* Default: stretch to fill container */
align-items: flex-start; /* Align to top */
align-items: flex-end; /* Align to bottom */
align-items: center; /* Center vertically */
align-items: baseline; /* Align text baselines */
}
The Flex Property Explained
The flex property is shorthand for three properties: flex-grow, flex-shrink, and flex-basis. Understanding these individually helps you write more precise layouts.
- flex-grow: How much the item should grow relative to other items (default: 0)
- flex-shrink: How much the item should shrink when space is limited (default: 1)
- flex-basis: The initial size before growing or shrinking (default: auto)
.item-grow {
flex: 1; /* Equivalent to: 1 1 0% */
}
.item-fixed {
flex: 0 0 200px; /* Don't grow or shrink, stay at 200px */
}
.item-shrink {
flex: 0 1 auto; /* Don't grow, but can shrink if needed */
}
Pro tip: Use flex: 1 when you want items to share space equally, and flex: 0 0 auto when you want items to maintain their natural size without growing or shrinking.
Grid Basics (2D Layout)
CSS Grid is built for two-dimensional layouts — controlling both rows and columns simultaneously. It's the go-to choice for page-level layouts, card grids, and any design where you need precise control over both dimensions.
Core Concepts
Grid layouts consist of a grid container and grid items. Unlike Flexbox, Grid lets you define explicit tracks (rows and columns) and place items anywhere within that structure:
/* The grid container */
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 3 columns */
grid-template-rows: auto 1fr auto; /* 3 rows */
gap: 1rem; /* Space between cells */
grid-auto-flow: row; /* How auto-placed items flow */
}
/* Grid items */
.item {
grid-column: 1 / 3; /* Span from column line 1 to 3 */
grid-row: 1 / 2; /* Occupy first row */
}
.item-area {
grid-area: header; /* Place in named area */
}
Understanding Grid Lines and Tracks
Grid creates numbered lines between tracks. A 3-column grid has 4 vertical lines (1, 2, 3, 4). Items are placed by specifying which lines they span between.
Tracks are the spaces between lines — your actual columns and rows. You can size them with fixed units (px), flexible units (fr), or content-based sizing (auto, min-content, max-content).
The FR Unit
The fr unit is Grid's superpower. It represents a fraction of available space, distributing it proportionally among tracks:
.container {
grid-template-columns: 1fr 2fr 1fr; /* Middle column gets 2x space */
grid-template-columns: 200px 1fr; /* Fixed sidebar, flexible content */
grid-template-columns: repeat(3, 1fr); /* Three equal columns */
}
Named Grid Areas
Grid's template areas feature lets you create layouts using ASCII art-like syntax, making complex layouts readable:
.container {
display: grid;
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer";
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto 1fr auto;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }
Auto-Placement and Grid Auto Flow
Grid can automatically place items you don't explicitly position. The grid-auto-flow property controls whether items fill rows first (default) or columns first:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: dense; /* Fill gaps with smaller items */
}
Pro tip: Use grid-auto-flow: dense for masonry-like layouts where you want to fill gaps, but be aware this can change the visual order of items, potentially affecting accessibility.
Side-by-Side Comparison
Let's compare Flexbox and Grid across key dimensions to understand their fundamental differences:
| Feature | Flexbox | Grid |
|---|---|---|
| Dimensionality | One-dimensional (row OR column) | Two-dimensional (rows AND columns) |
| Content-driven | Yes - items control their size | No - container defines structure |
| Layout direction | Single axis at a time | Both axes simultaneously |
| Item placement | Sequential (source order) | Explicit positioning possible |
| Alignment | Powerful on single axis | Powerful on both axes |
| Gap support | Yes (modern browsers) | Yes (from the start) |
| Best for | Components, navigation, toolbars | Page layouts, card grids, dashboards |
| Learning curve | Moderate | Steeper initially |
Conceptual Differences
The fundamental difference comes down to control. Flexbox is content-out — items influence the layout based on their content size. Grid is layout-in — you define the structure first, then place content into it.
Think of Flexbox as a flexible ruler that adjusts to what you're measuring. Grid is more like graph paper where you decide the structure upfront.
| Scenario | Flexbox Approach | Grid Approach |
|---|---|---|
| Navigation bar | Natural fit - items in a row | Overkill - too much structure |
| Card grid | Possible but awkward wrapping | Perfect - explicit rows and columns |
| Form layout | Good for simple forms | Better for complex multi-column forms |
| Page layout | Requires nesting multiple containers | Single container can handle it |
| Centering content | Excellent - simple and intuitive | Also excellent - place-items: center |
When to Use Flexbox
Flexbox shines in scenarios where content flows in a single direction and items need to adapt to available space. Here are the situations where Flexbox is your best choice:
Navigation Bars and Menus
Navigation components are the poster child for Flexbox. Items naturally flow in a row, and you often want them to distribute space evenly or align to opposite ends:
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
.nav-links {
display: flex;
gap: 2rem;
list-style: none;
}
Button Groups and Toolbars
When you have a collection of buttons or tools that should sit side-by-side, Flexbox handles the spacing and alignment perfectly:
.button-group {
display: flex;
gap: 0.5rem;
}
.toolbar {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 1rem;
}
Media Objects
The classic media object pattern (image on one side, content on the other) is trivial with Flexbox:
.media {
display: flex;
gap: 1rem;
}
.media-image {
flex: 0 0 100px; /* Fixed width image */
}
.media-content {
flex: 1; /* Content takes remaining space */
}
Form Controls
Input groups where a label, input, and button sit in a row are perfect for Flexbox:
.input-group {
display: flex;
gap: 0.5rem;
}
.input-group input {
flex: 1; /* Input grows to fill space */
}
.input-group button {
flex: 0 0 auto; /* Button stays at natural size */
}
Vertical Centering
Flexbox makes vertical centering trivial, solving one of CSS's historic pain points:
.center-content {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Quick tip: If you find yourself wrapping Flexbox containers inside other Flexbox containers more than two levels deep, consider whether Grid might be a better choice for the outer structure.
When to Use Grid
Grid excels when you need to control layout in two dimensions simultaneously. Here's when to reach for Grid:
Page Layouts
Grid was designed for page-level layouts. The classic header-sidebar-content-footer structure is Grid's sweet spot:
.page {
display: grid;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
Card Grids and Galleries
When you need items arranged in rows and columns with consistent sizing, Grid is unbeatable:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
The auto-fit and minmax() combination creates responsive grids without media queries.
Complex Forms
Multi-column forms with labels, inputs, and helper text spanning different widths are much easier with Grid:
.form-grid {
display: grid;
grid-template-columns: 150px 1fr 1fr;
gap: 1rem;
align-items: center;
}
.full-width {
grid-column: 1 / -1; /* Span all columns */
}
Dashboard Layouts
Dashboards with widgets of varying sizes are Grid's domain. You can create complex layouts where items span multiple rows and columns:
.dashboard {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-rows: 100px;
gap: 1rem;
}
.widget-large {
grid-column: span 8;
grid-row: span 2;
}
.widget-small {
grid-column: span 4;
grid-row: span 1;
}
Magazine-Style Layouts
When you need asymmetric layouts where content pieces have different sizes and positions, Grid gives you precise control:
.magazine {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-auto-rows: 200px;
gap: 1rem;
}
.featured {
grid-column: 1 / 4;
grid-row: 1 / 3;
}
.secondary {
grid-column: 4 / 7;
grid-row: 1 / 2;
}
Pro tip: Use Grid's repeat(auto-fit, minmax(...)) pattern for responsive layouts that adapt without media queries. This is one of Grid's most powerful features.
Real Layout Examples
Let's look at complete, real-world examples that demonstrate when to use each layout system.
Example 1: Blog Post Header (Flexbox)
A blog post header with metadata arranged horizontally is perfect for Flexbox:
<header class="post-header">
<div class="post-meta">
<img src="avatar.jpg" alt="Author" class="avatar">
<div class="author-info">
<span class="author-name">Jane Doe</span>
<span class="post-date">March 31, 2026</span>
</div>
</div>
<div class="post-actions">
<button>Share</button>
<button>Bookmark</button>
</div>
</header>
.post-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid #e5e7eb;
}
.post-meta {
display: flex;
align-items: center;
gap: 1rem;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
}
.author-info {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.post-actions {
display: flex;
gap: 0.5rem;
}
Example 2: Product Grid (Grid)
A responsive product grid that maintains consistent columns is Grid's territory:
<div class="product-grid">
<article class="product-card">...</article>
<article class="product-card">...</article>
<article class="product-card">...</article>
</div>
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
padding: 2rem;
}
.product-card {
display: flex;
flex-direction: column;
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
}
.product-card img {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
}
.product-info {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
Notice how we use Grid for the overall layout but Flexbox inside each card for the vertical arrangement of content.
Example 3: App Layout (Grid + Flexbox)
A complete application layout demonstrates how Grid and Flexbox work together:
<div class="app">
<header class="app-header">...</header>
<aside class="app-sidebar">...</aside>
<main class="app-content">...</main>
<footer class="app-footer">...</footer>
</div>
.app {
display: grid;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: 60px 1fr auto;
min-height: 100vh;
}
.app-header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1rem;
background: #1e293b;
color: white;
}
.app-sidebar {
grid-area: sidebar;
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 1rem;
background: #f8fafc;
}
.app-content {
grid-area: content;
padding: 2rem;
}
.app-footer {
grid-area: footer;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
background: #f8fafc;
}
Combining Flexbox and Grid
The most powerful layouts use both Grid and Flexbox together. Grid handles the macro layout, while Flexbox manages the micro layouts within components.
The Hybrid Approach
Think of Grid as your structural skeleton and Flexbox as the connective tissue. Grid defines the major regions of your page, and Flexbox arranges content within those regions.
This separation of concerns makes your CSS more maintainable and your layouts more flexible.
Practical Hybrid Pattern
/* Grid for page structure */
.page-layout {
display: grid;
grid-template-columns: 300px 1fr;
gap: 2rem;
}
/* Flexbox for sidebar navigation */
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
/* Grid for content cards */
.content-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
/* Flexbox inside each card */
.card {
display: flex;
flex-direction: column;
gap: 1rem;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-footer {
display: flex;
gap: 0.5rem;
margin-top: auto;
}
When to Nest Which
A good rule of thumb: use Grid for the outer container when you need two-dimensional control, then use Flexbox for inner components that flow in one direction.
- Grid → Flexbox: Page layout (Grid) containing navigation bars (Flexbox)
- Grid → Grid: Dashboard (Grid) containing widget grids (Grid)
- Flexbox → Flexbox: Navigation (Flexbox) containing dropdown menus (Flexbox)
- Flexbox → Grid: Sidebar (Flexbox) containing a grid of icons (Grid)
Pro tip: Don't overthink it. Start with the outermost container and ask: "Do I need to control rows AND columns?" If yes, use Grid. If no, use Flexbox. Then repeat for each nested container.
Performance Considerations
Both Flexbox and Grid are highly optimized in modern browsers, but there are subtle performance differences worth understanding.
Rendering Performance
Grid generally performs slightly better for complex layouts because the browser can calculate the entire layout in one pass. Flexbox may require multiple passes when items have intrinsic sizing or when wrapping is involved.
However, these differences are negligible for most real-world applications. Don't choose based on performance unless you're building something with thousands of layout items.
Layout Recalculation
Flexbox can trigger more layout recalculations when content changes because items influence each other's sizes. Grid's explicit structure means changes are more contained.
If you're building a highly dynamic interface where content frequently changes, Grid might offer slightly better performance for the outer structure.
Best Practices for Performance
- Avoid deeply nested flex containers (more than 3-4 levels)
- Use
will-changesparingly and only when necessary - Prefer
gapover margins for spacing (better performance) - Use
content-visibility: autofor long lists or grids - Avoid percentage-based flex-basis values when possible
Responsive Design Patterns
Both layout systems offer powerful responsive capabilities, but they approach it differently.