Real-World Flexbox Layouts
From Theory to Production
In the previous lessons, you learned the individual properties of Flexbox -- both container and item properties. Now it is time to put them all together and build real-world layouts that you will encounter in professional web development every day. This lesson is entirely project-based. We will build ten complete layout patterns from scratch, each solving a specific design problem that frontend developers face regularly. By the end of this lesson, you will have a library of battle-tested Flexbox patterns that you can adapt and reuse in any project.
Each pattern in this lesson includes the complete HTML structure and CSS, an explanation of why specific flex properties are chosen, and notes on responsive behavior. We will cover responsive navigation bars, equal-height card grids, the holy grail layout, the media object pattern, sticky footers, perfect centering, responsive image galleries, form layouts, pricing tables, and dashboard layouts. We will also discuss when to choose Flexbox over CSS Grid and vice versa.
Pattern 1: Responsive Navbar with Flexbox
A navigation bar is one of the most common components on any website. A well-built navbar aligns a logo on the left with navigation links on the right, centers vertically, and collapses into a mobile-friendly layout on smaller screens. Flexbox makes this straightforward.
HTML: Responsive Navbar
<header class="navbar">
<a href="/" class="navbar-brand">
<img src="logo.svg" alt="Company Logo" width="120" height="40">
</a>
<nav class="navbar-links">
<a href="/products">Products</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
</nav>
<div class="navbar-actions">
<a href="/login" class="btn-login">Log In</a>
<a href="/signup" class="btn-signup">Sign Up</a>
</div>
</header>
CSS: Responsive Navbar
.navbar {
display: flex;
align-items: center;
padding: 0 24px;
height: 64px;
background: var(--bg-white);
border-bottom: 1px solid var(--border-light);
}
.navbar-brand {
flex: none; /* Logo never grows or shrinks */
}
.navbar-links {
display: flex;
gap: 32px;
margin-left: 48px; /* Space between logo and links */
}
.navbar-links a {
text-decoration: none;
color: var(--text-dark);
font-weight: 500;
white-space: nowrap;
}
.navbar-actions {
display: flex;
gap: 12px;
margin-left: auto; /* Pushes actions to the far right */
}
.btn-login {
padding: 8px 16px;
color: var(--primary);
text-decoration: none;
}
.btn-signup {
padding: 8px 16px;
background: var(--primary);
color: white;
border-radius: 6px;
text-decoration: none;
}
/* Tablet: hide actions, keep links */
@media (max-width: 900px) {
.navbar-links {
gap: 20px;
margin-left: 24px;
}
.navbar-actions {
display: none;
}
}
/* Mobile: stack everything vertically */
@media (max-width: 600px) {
.navbar {
flex-wrap: wrap;
height: auto;
padding: 12px 16px;
}
.navbar-brand {
flex: 1; /* Takes full width of first row */
}
.navbar-links {
flex-basis: 100%; /* Forces onto new row */
margin-left: 0;
margin-top: 12px;
gap: 16px;
justify-content: space-between;
}
}
The key techniques here are margin-left: auto on the actions group to push it to the right, flex: none on the logo to keep it rigid, and flex-basis: 100% on mobile to force the links onto a new row when the navbar wraps. This is a mobile-first pattern that degrades gracefully.
Pattern 2: Card Layout with Equal-Height Cards
Cards are the most ubiquitous UI component in modern web design. The challenge is making all cards the same height regardless of their content, while keeping the card footer pinned to the bottom. This is where the combination of a flex container and flex column items shines.
HTML: Equal-Height Card Grid
<div class="card-grid">
<article class="card">
<img src="image1.jpg" alt="Feature 1" class="card-image">
<div class="card-content">
<h3>Short Title</h3>
<p>Brief description.</p>
</div>
<div class="card-footer">
<a href="#">Read More</a>
</div>
</article>
<article class="card">
<img src="image2.jpg" alt="Feature 2" class="card-image">
<div class="card-content">
<h3>A Much Longer Title That Wraps</h3>
<p>This card has significantly more text content which makes
it taller than the other cards naturally.</p>
</div>
<div class="card-footer">
<a href="#">Read More</a>
</div>
</article>
<article class="card">
<img src="image3.jpg" alt="Feature 3" class="card-image">
<div class="card-content">
<h3>Medium Title</h3>
<p>A moderate amount of text.</p>
</div>
<div class="card-footer">
<a href="#">Read More</a>
</div>
</article>
</div>
CSS: Equal-Height Card Grid
.card-grid {
display: flex;
gap: 24px;
padding: 24px;
}
.card {
flex: 1;
display: flex;
flex-direction: column;
background: var(--bg-white);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
flex: 1; /* Grows to fill remaining space */
padding: 20px;
}
.card-content h3 {
margin: 0 0 8px;
}
.card-content p {
margin: 0;
color: var(--text-light);
}
.card-footer {
padding: 16px 20px;
border-top: 1px solid var(--border-light);
}
.card-footer a {
color: var(--primary);
font-weight: 600;
text-decoration: none;
}
/* Responsive: 2 columns on tablet */
@media (max-width: 900px) {
.card-grid {
flex-wrap: wrap;
}
.card {
flex: 1 1 calc(50% - 12px);
min-width: 280px;
}
}
/* Responsive: single column on mobile */
@media (max-width: 600px) {
.card {
flex: 1 1 100%;
}
}
The outer .card-grid is a flex row. Each .card uses flex: 1 to share space equally and display: flex; flex-direction: column to stack its contents vertically. The .card-content has flex: 1 which pushes the footer to the bottom of every card, regardless of how much text the card contains. The default align-items: stretch on the grid container ensures all cards are the same height.
calc(50% - 12px) in the tablet breakpoint accounts for the gap. With a 24px gap between two items, each item needs to be half the container minus half the gap (12px). This ensures the items wrap correctly to exactly two columns.Pattern 3: Holy Grail Layout
The holy grail layout is a full-page layout with a fixed header, fixed footer, and a three-column middle section. The main content area is fluid while the two sidebars have fixed widths. The content area should fill the remaining viewport height when the content is short.
HTML: Holy Grail Layout
<div class="page-layout">
<header class="page-header">
<h1>Site Title</h1>
<nav>Home | About | Contact</nav>
</header>
<div class="page-body">
<aside class="sidebar-left">
<h3>Navigation</h3>
<ul>
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Reports</a></li>
</ul>
</aside>
<main class="main-content">
<h2>Main Content Area</h2>
<p>This area expands to fill all available space.</p>
</main>
<aside class="sidebar-right">
<h3>Widgets</h3>
<p>Recent posts, ads, etc.</p>
</aside>
</div>
<footer class="page-footer">
<p>Copyright 2025. All rights reserved.</p>
</footer>
</div>
CSS: Holy Grail Layout
.page-layout {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.page-header {
flex: none;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: var(--primary);
color: white;
}
.page-body {
flex: 1; /* Fills remaining height */
display: flex; /* Nested flex for columns */
}
.sidebar-left {
flex: 0 0 220px; /* Fixed width, no grow, no shrink */
padding: 20px;
background: #f0f2f5;
border-right: 1px solid var(--border-light);
}
.main-content {
flex: 1; /* Fluid center column */
padding: 24px;
min-width: 0; /* Prevent overflow */
}
.sidebar-right {
flex: 0 0 200px; /* Fixed width right sidebar */
padding: 20px;
background: #f0f2f5;
border-left: 1px solid var(--border-light);
}
.page-footer {
flex: none;
padding: 16px 24px;
background: var(--text-dark);
color: white;
text-align: center;
}
/* Responsive: drop right sidebar first */
@media (max-width: 1024px) {
.sidebar-right {
display: none;
}
}
/* Responsive: stack everything on mobile */
@media (max-width: 768px) {
.page-body {
flex-direction: column;
}
.sidebar-left {
flex-basis: auto;
border-right: none;
border-bottom: 1px solid var(--border-light);
}
}
Pattern 4: Media Object Pattern
The media object is one of the most reusable components in web design. It places an image or avatar beside a block of text content. You will find this pattern in comment threads, notification lists, chat messages, user profiles, and product listings. The key requirement is that the image stays a fixed size while the text area fills the remaining width.
HTML and CSS: Complete Media Object
/* HTML */
<div class="comment">
<img class="comment-avatar" src="user.jpg" alt="User name">
<div class="comment-body">
<div class="comment-meta">
<strong>Jane Smith</strong>
<time>2 hours ago</time>
</div>
<p>This is a comment that demonstrates the media object pattern.
The text wraps naturally within the available space.</p>
<div class="comment-actions">
<button>Reply</button>
<button>Like</button>
</div>
</div>
</div>
/* CSS */
.comment {
display: flex;
gap: 16px;
padding: 16px;
align-items: flex-start;
}
.comment-avatar {
flex: none;
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
}
.comment-body {
flex: 1;
min-width: 0;
}
.comment-meta {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 4px;
}
.comment-meta time {
color: var(--text-light);
font-size: 0.875rem;
}
.comment-actions {
display: flex;
gap: 12px;
margin-top: 8px;
}
.comment-actions button {
background: none;
border: none;
color: var(--text-light);
cursor: pointer;
font-size: 0.875rem;
padding: 0;
}
/* Nested media object (reply) */
.comment .comment {
margin-top: 16px;
padding-left: 0;
border-left: 2px solid var(--border-light);
padding-left: 16px;
}
Pattern 5: Sticky Footer
The sticky footer pattern ensures the footer stays at the bottom of the viewport when the page content does not fill the screen, but flows naturally below the content when the content exceeds the viewport height. This is one of the cleanest applications of Flexbox.
CSS: Sticky Footer (Minimal)
/* Apply to the body or a wrapper element */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
/* Header and footer stay at natural size */
header, footer {
flex: none;
}
/* Main content grows to push footer down */
main {
flex: 1;
}
/* That is it. Three lines on the key elements. */
This pattern works because min-height: 100vh ensures the flex container is at least as tall as the viewport. The flex: 1 on main tells it to grow and absorb all remaining space. When the content is short, main expands to fill the gap; when content is long, main takes its natural height and the page scrolls normally.
Pattern 6: Perfect Centering
Centering content both horizontally and vertically has historically been one of the most frustrating tasks in CSS. Flexbox solves it elegantly with just three lines. Here are multiple approaches for different scenarios.
CSS: Centering Techniques
/* Method 1: justify-content + align-items (most common) */
.center-method-1 {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Method 2: margin: auto on the child (also perfect) */
.center-method-2 {
display: flex;
min-height: 100vh;
}
.center-method-2 .child {
margin: auto;
}
/* Method 3: place-content shorthand */
.center-method-3 {
display: flex;
place-content: center;
min-height: 100vh;
}
/* Centering a login form on the page */
.login-page {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: var(--bg-light);
padding: 20px;
}
.login-card {
flex: none;
width: 100%;
max-width: 400px;
padding: 32px;
background: var(--bg-white);
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
}
margin: auto approach absorbs free space into the margins, effectively centering the child. The justify-content + align-items approach distributes free space around the child. Both achieve the same visual result for a single centered item, but margin: auto is more flexible when you need to center one item while other items exist in the container.Pattern 7: Responsive Image Gallery with flex-wrap
An image gallery needs to display thumbnails in a flexible grid that wraps to multiple rows and adapts to different screen sizes. This is where flex-wrap combined with flex-basis creates a responsive grid without media queries.
HTML: Image Gallery
<div class="gallery">
<figure class="gallery-item">
<img src="photo1.jpg" alt="Gallery photo 1">
<figcaption>Mountain Sunset</figcaption>
</figure>
<figure class="gallery-item">
<img src="photo2.jpg" alt="Gallery photo 2">
<figcaption>Ocean Waves</figcaption>
</figure>
<!-- More items... -->
</div>
CSS: Responsive Gallery
.gallery {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 16px;
}
.gallery-item {
flex: 1 1 280px; /* Grow, shrink, minimum 280px */
margin: 0;
overflow: hidden;
border-radius: 8px;
background: var(--bg-white);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
.gallery-item figcaption {
padding: 12px 16px;
font-size: 0.9rem;
color: var(--text-dark);
}
/* Prevent last row items from stretching too wide */
.gallery::after {
content: "";
flex: 1 1 280px;
max-height: 0;
overflow: hidden;
}
The magic is in flex: 1 1 280px. Each item has a minimum width of 280px. When the container is wide enough for four items, you get four columns. When it shrinks, items wrap to the next row. The flex-grow: 1 ensures items stretch to fill each row. The pseudo-element hack prevents the last row from having items that stretch across the full width when only one or two items remain.
::after pseudo-element, if you have 5 items and the layout shows 3 per row, the last row will have 2 items that stretch to fill the entire row width, making them much wider than the items above. The invisible pseudo-element acts as a phantom third item that prevents this stretching. For more control over grid-like layouts, consider CSS Grid instead.Pattern 8: Form Layout with Flexbox
Forms benefit greatly from Flexbox, especially for inline label-input pairs, button groups, and multi-column form sections. Here is a complete contact form layout built with Flexbox.
HTML: Form Layout
<form class="flex-form">
<div class="form-row">
<div class="form-group">
<label for="first-name">First Name</label>
<input type="text" id="first-name" name="first-name" required>
</div>
<div class="form-group">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name" required>
</div>
</div>
<div class="form-group full-width">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group full-width">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5"></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn-secondary">Cancel</button>
<button type="submit" class="btn-primary">Send Message</button>
</div>
</form>
CSS: Form Layout
.flex-form {
max-width: 640px;
margin: 0 auto;
padding: 32px;
}
.form-row {
display: flex;
gap: 16px;
}
.form-group {
flex: 1;
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.form-group.full-width {
flex-basis: 100%;
}
.form-group label {
margin-bottom: 6px;
font-weight: 600;
font-size: 0.9rem;
color: var(--text-dark);
}
.form-group input,
.form-group textarea {
padding: 10px 14px;
border: 1px solid var(--border-light);
border-radius: 6px;
font-size: 1rem;
font-family: inherit;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 8px;
}
.btn-primary {
padding: 10px 24px;
background: var(--primary);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
}
.btn-secondary {
padding: 10px 24px;
background: transparent;
color: var(--text-dark);
border: 1px solid var(--border-light);
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
}
/* Responsive: stack form rows on mobile */
@media (max-width: 500px) {
.form-row {
flex-direction: column;
}
.form-actions {
flex-direction: column-reverse;
}
.form-actions button {
width: 100%;
}
}
The .form-row places first name and last name side by side using display: flex. Each .form-group is also a flex column to stack the label above its input. The .form-actions uses justify-content: flex-end to push the buttons to the right. On mobile, the form rows stack vertically and the buttons go full width with column-reverse so the primary action appears first visually.
Pattern 9: Pricing Table
Pricing tables need to display plan options side by side with equal heights, highlighted recommended plan, and feature lists that align across columns. Flexbox handles this elegantly.
HTML: Pricing Table
<div class="pricing-table">
<div class="pricing-card">
<div class="pricing-header">
<h3>Basic</h3>
<div class="price">$9<span>/month</span></div>
</div>
<ul class="pricing-features">
<li>5 Projects</li>
<li>10GB Storage</li>
<li>Email Support</li>
</ul>
<div class="pricing-action">
<button>Choose Basic</button>
</div>
</div>
<div class="pricing-card featured">
<div class="pricing-header">
<h3>Professional</h3>
<div class="price">$29<span>/month</span></div>
</div>
<ul class="pricing-features">
<li>Unlimited Projects</li>
<li>100GB Storage</li>
<li>Priority Support</li>
<li>Advanced Analytics</li>
</ul>
<div class="pricing-action">
<button>Choose Professional</button>
</div>
</div>
<div class="pricing-card">
<div class="pricing-header">
<h3>Enterprise</h3>
<div class="price">$79<span>/month</span></div>
</div>
<ul class="pricing-features">
<li>Unlimited Everything</li>
<li>1TB Storage</li>
<li>24/7 Phone Support</li>
<li>Custom Integrations</li>
<li>SLA Guarantee</li>
</ul>
<div class="pricing-action">
<button>Choose Enterprise</button>
</div>
</div>
</div>
CSS: Pricing Table
.pricing-table {
display: flex;
gap: 24px;
padding: 40px 24px;
align-items: stretch;
max-width: 1000px;
margin: 0 auto;
}
.pricing-card {
flex: 1;
display: flex;
flex-direction: column;
background: var(--bg-white);
border: 1px solid var(--border-light);
border-radius: 12px;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
}
.pricing-card.featured {
border-color: var(--primary);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
transform: scale(1.05);
position: relative;
}
.pricing-header {
padding: 32px 24px;
text-align: center;
background: var(--bg-light);
}
.pricing-card.featured .pricing-header {
background: var(--primary);
color: white;
}
.price {
font-size: 3rem;
font-weight: 700;
margin-top: 8px;
}
.price span {
font-size: 1rem;
font-weight: 400;
opacity: 0.7;
}
.pricing-features {
flex: 1; /* Pushes action button to bottom */
list-style: none;
padding: 24px;
margin: 0;
}
.pricing-features li {
padding: 8px 0;
border-bottom: 1px solid var(--border-light);
}
.pricing-features li:last-child {
border-bottom: none;
}
.pricing-action {
padding: 24px;
}
.pricing-action button {
width: 100%;
padding: 14px;
border: 2px solid var(--primary);
background: transparent;
color: var(--primary);
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
.pricing-card.featured .pricing-action button {
background: var(--primary);
color: white;
}
/* Responsive */
@media (max-width: 768px) {
.pricing-table {
flex-direction: column;
max-width: 400px;
}
.pricing-card.featured {
transform: none;
order: -1; /* Featured card first on mobile */
}
}
The flex: 1 on .pricing-features is the key detail. It makes the feature list grow to fill available vertical space, which pushes the action button to the bottom of every card. This keeps all the call-to-action buttons aligned at the same height even when cards have different numbers of features.
Pattern 10: Dashboard Sidebar + Content
Dashboard layouts typically have a fixed sidebar for navigation and a fluid content area that fills the remaining space. The sidebar should remain visible while the content area scrolls independently.
HTML: Dashboard Layout
<div class="dashboard">
<aside class="dashboard-sidebar">
<div class="sidebar-brand">Dashboard</div>
<nav class="sidebar-nav">
<a href="#" class="active">Overview</a>
<a href="#">Analytics</a>
<a href="#">Users</a>
<a href="#">Settings</a>
</nav>
<div class="sidebar-footer">
<a href="#">Log Out</a>
</div>
</aside>
<div class="dashboard-main">
<header class="dashboard-header">
<h1>Overview</h1>
<div class="user-menu">John Doe</div>
</header>
<div class="dashboard-content">
<!-- Dashboard widgets and content here -->
</div>
</div>
</div>
CSS: Dashboard Layout
.dashboard {
display: flex;
height: 100vh;
overflow: hidden;
}
.dashboard-sidebar {
flex: 0 0 260px; /* Fixed width sidebar */
display: flex;
flex-direction: column;
background: var(--text-dark);
color: white;
overflow-y: auto; /* Sidebar scrolls independently */
}
.sidebar-brand {
flex: none;
padding: 20px 24px;
font-size: 1.25rem;
font-weight: 700;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-nav {
flex: 1; /* Nav grows to fill space */
display: flex;
flex-direction: column;
padding: 12px 0;
}
.sidebar-nav a {
padding: 12px 24px;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
}
.sidebar-nav a.active {
color: white;
background: rgba(255, 255, 255, 0.1);
border-left: 3px solid var(--primary);
}
.sidebar-footer {
flex: none; /* Footer stays at bottom */
padding: 16px 24px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-footer a {
color: rgba(255, 255, 255, 0.5);
text-decoration: none;
}
.dashboard-main {
flex: 1; /* Content fills remaining space */
display: flex;
flex-direction: column;
min-width: 0; /* Prevent overflow */
overflow: hidden;
}
.dashboard-header {
flex: none;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: var(--bg-white);
border-bottom: 1px solid var(--border-light);
}
.dashboard-content {
flex: 1;
padding: 24px;
overflow-y: auto; /* Content scrolls independently */
background: var(--bg-light);
}
/* Responsive: overlay sidebar on mobile */
@media (max-width: 768px) {
.dashboard-sidebar {
position: fixed;
left: -260px; /* Hidden off-screen by default */
top: 0;
bottom: 0;
z-index: 100;
transition: left 0.3s ease;
}
.dashboard-sidebar.open {
left: 0; /* Slide in when toggled */
}
}
This pattern uses nested flex containers extensively. The outer dashboard is a flex row with the sidebar and main area side by side. The sidebar itself is a flex column with the nav growing to fill space and the footer pinned to the bottom via flex: 1 on the nav section. The main area is also a flex column with the content area growing and scrolling independently. The overflow: hidden on the dashboard and overflow-y: auto on the content area create independent scroll regions.
Mobile-First Patterns
Mobile-first design means starting with the mobile layout and progressively enhancing for larger screens. With Flexbox, the mobile layout is typically a single column (which is the natural document flow), and you add horizontal layouts at wider breakpoints.
Example: Mobile-First Approach
/* Mobile: single column (default flow, no flex needed) */
.feature-section {
padding: 20px;
}
.feature-item {
margin-bottom: 24px;
}
/* Tablet and up: side by side with Flexbox */
@media (min-width: 600px) {
.feature-section {
display: flex;
flex-wrap: wrap;
gap: 24px;
}
.feature-item {
flex: 1 1 calc(50% - 12px);
margin-bottom: 0;
}
}
/* Desktop: three columns */
@media (min-width: 900px) {
.feature-item {
flex: 1 1 calc(33.333% - 16px);
}
}
display: flex at all for the mobile layout. Only add Flexbox at the breakpoint where you need horizontal arrangements. This keeps your mobile CSS lighter and faster to parse.When to Choose Flexbox vs Grid
Both Flexbox and CSS Grid are modern layout tools, but they solve different problems. Understanding when to use each one will make you a more effective developer.
Use Flexbox When:
- You have a one-dimensional layout -- Items flow in a single row OR a single column. Navigation bars, button groups, media objects, and card rows are all one-dimensional patterns.
- Content size should drive the layout -- When you want items to grow, shrink, and wrap naturally based on their content and the available space. Flexbox excels when item sizes are unknown or dynamic.
- You need alignment within a row or column -- Centering items, distributing space between them, or aligning items along a cross axis are Flexbox strengths.
- You need to reorder items visually -- The
orderproperty makes it easy to rearrange items without changing HTML. - Components inside a larger layout -- Navbar internals, card contents, form groups, button bars, and other component-level layouts are ideal for Flexbox.
Use CSS Grid When:
- You have a two-dimensional layout -- When you need to control both rows AND columns simultaneously. Page-level layouts with headers, sidebars, and content areas in a grid structure.
- You want items to align to a predefined grid -- When the grid structure matters more than the content size. Magazine-style layouts, dashboards, and image mosaic patterns.
- You need items to span multiple rows or columns -- Grid makes it trivial for items to occupy 2 rows and 3 columns, which is awkward with Flexbox.
- You want gap control in both dimensions -- While Flexbox supports gaps, Grid gives you independent control over row and column gaps in a true two-dimensional grid.
Use Both Together:
The best layouts combine Grid for the page-level structure and Flexbox for component-level details. For example, use Grid to define the header, sidebar, main content, and footer areas of a page, then use Flexbox inside the header to lay out the navbar, inside the main content for card rows, and inside the sidebar for the navigation list.
Example: Grid for Page, Flexbox for Components
/* Page layout with Grid */
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 64px 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
min-height: 100vh;
}
/* Navbar component with Flexbox */
.header {
grid-area: header;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}
/* Card grid inside main with Flexbox */
.main {
grid-area: main;
padding: 24px;
}
.card-row {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.card-row .card {
flex: 1 1 300px;
}
/* Sidebar navigation with Flexbox */
.sidebar {
grid-area: sidebar;
display: flex;
flex-direction: column;
}
.sidebar nav {
flex: 1;
display: flex;
flex-direction: column;
}
flex-wrap extensively with percentage-based flex-basis values and complex calc() expressions to simulate a grid, switch to CSS Grid. Similarly, do not use Grid for simple one-dimensional layouts like centering a button or spacing out three navigation links -- Flexbox handles those more naturally.Summary of Flexbox Layout Patterns
Here is a quick reference of the patterns we covered and the key flex properties that make them work:
- Navbar --
margin-left: autoto push actions right,flex: nonefor logo. - Equal-height cards -- Cards use
flex: 1+flex-direction: column, content usesflex: 1to push footer down. - Holy grail -- Outer column flex with
flex: 1on body, inner row flex withflex: 0 0 <width>sidebars. - Media object --
flex: noneon image,flex: 1+min-width: 0on text body. - Sticky footer -- Column flex on body with
min-height: 100vh,flex: 1on main. - Centering --
justify-content: center+align-items: centerormargin: auto. - Image gallery --
flex-wrap: wrapwithflex: 1 1 <min-width>. - Forms --
flex: 1on groups in a row,flex-direction: columnfor label + input stacks. - Pricing table --
flex: 1on feature lists to align action buttons,order: -1for mobile featured plan. - Dashboard --
flex: 0 0 <width>sidebar, nested column flex with independent scrolling.
Practice Exercise
Build a complete single-page portfolio website using only Flexbox for layout. Start with a sticky footer structure (header, main, footer). Inside the header, create a responsive navbar with a logo on the left and navigation links pushed to the right using margin-left: auto. In the main area, create four sections: (1) a hero section with centered content using justify-content: center and align-items: center, (2) a features section with three equal-height cards using the card grid pattern, (3) a testimonials section using the media object pattern for each testimonial, and (4) a contact form using the Flexbox form layout pattern with first name and last name side by side. Make the layout responsive: on mobile, stack everything vertically; on tablet, show two cards per row; on desktop, show three cards per row. Test on multiple screen sizes to verify the footer sticks correctly, the cards maintain equal heights, and the navbar collapses gracefully.