CSS Grid vs Flexbox: The Definitive Guide (2026)

Stop guessing which CSS layout system to use. This guide gives you clear rules, side-by-side comparisons, and production-ready patterns for building any layout with Grid, Flexbox, or both.

The Fundamental Difference

The single most important thing to understand about CSS Grid and Flexbox is the axis model:

This is not a matter of one being "better" than the other. They solve different problems, and the best layouts use both. Think of it this way: Grid defines the structure, Flexbox aligns the content.

Flexbox (1D)

A
B (2x)
C

Grid (2D)

A
B (spans 2)
C (spans 2)
D

In the Flexbox example, items distribute space along a single row. In the Grid example, items are placed in both rows and columns, and can span across cells. Both are powerful. The key is knowing which to reach for.

When to Use Flexbox

Flexbox excels when you need to distribute space among items along a single axis. Here are the most common use cases:

Navigation Bars

A navigation bar is a row of items that need to be spaced and aligned. Flexbox handles this perfectly.

css
.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  height: 64px;
}

.navbar-logo {
  font-weight: 700;
  font-size: 1.25rem;
}

.navbar-links {
  display: flex;
  gap: 24px;
  list-style: none;
}

.navbar-actions {
  display: flex;
  gap: 12px;
  align-items: center;
}

This creates a nav bar with the logo on the left, links in the center, and action buttons on the right. The space-between pushes the three groups apart, and each group internally uses flex with gap to space its children.

Button Groups and Toolbars

css
.button-group {
  display: flex;
  gap: 8px;
  align-items: center;
}

/* Make one button fill remaining space */
.button-group .primary {
  flex: 1;
}

/* Vertical toolbar */
.toolbar {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 48px;
}

Centering Content

The classic "center both horizontally and vertically" pattern is a Flexbox one-liner:

css
.centered {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

Card Content Alignment

Inside a card, Flexbox ensures the footer sticks to the bottom regardless of content height:

css
.card {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.card-body {
  flex: 1; /* Takes up all available space */
}

.card-footer {
  margin-top: auto; /* Pushes to the bottom */
}
Quick Rule

If you are arranging items in a line (horizontal or vertical) and the relationship between items is about distributing space, reach for Flexbox first.

When to Use CSS Grid

CSS Grid shines when you need to control placement in two dimensions or when you want the layout to be defined by the container rather than the content.

Page Layouts

The classic header/sidebar/main/footer page layout is a Grid pattern:

css
.page-layout {
  display: grid;
  grid-template-columns: 280px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

/* Collapse sidebar on mobile */
@media (max-width: 768px) {
  .page-layout {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"
      "footer";
  }
  .sidebar { display: none; }
}

The grid-template-areas property makes the layout readable at a glance. You can see the structure just by reading the CSS. Try building this with Flexbox and you will appreciate why Grid exists.

Card Grids

Responsive card grids are where Grid truly outshines Flexbox. The auto-fill and minmax combination creates a responsive grid without any media queries:

css
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
}

/* Cards automatically reflow:
   - 4 columns on wide screens
   - 3 columns on medium screens
   - 2 columns on tablets
   - 1 column on mobile
   All without a single media query. */

Need to generate this CSS visually? Use NexTool's CSS Grid Generator to build grid layouts with a point-and-click interface and export production-ready code.

Dashboard Layouts

css
.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: auto;
  gap: 16px;
}

/* A widget that spans 2 columns */
.widget-wide {
  grid-column: span 2;
}

/* A widget that spans 2 rows */
.widget-tall {
  grid-row: span 2;
}

/* Featured widget: 2x2 */
.widget-featured {
  grid-column: span 2;
  grid-row: span 2;
}

Form Layouts

Forms with labels and inputs aligned in columns are a Grid use case:

css
.form-grid {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 12px 16px;
  align-items: center;
}

/* Full-width elements (like submit buttons) */
.form-grid .full-width {
  grid-column: 1 / -1;
}

/* Two inputs side by side */
.form-row {
  grid-column: 2;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

Side-by-Side Comparisons

Let us compare how the same layout is built with each approach. This makes the differences tangible.

Comparison 1: Equal-Height Columns

Flexbox

.columns {
  display: flex;
  gap: 24px;
}
.column {
  flex: 1;
  /* Height automatically
     matches tallest sibling */
}

Grid

.columns {
  display: grid;
  grid-template-columns:
    repeat(3, 1fr);
  gap: 24px;
  /* Equal height by default */
}

Verdict: Both work equally well here. Flexbox is slightly more concise but less explicit about column count. Grid forces you to declare the structure upfront, which can be clearer.

Comparison 2: Responsive Wrapping

Flexbox

.items {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}
.item {
  flex: 1 1 300px;
  /* Grows, shrinks,
     min 300px base */
}
/* Items may have uneven
   widths on the last row */

Grid

.items {
  display: grid;
  grid-template-columns:
    repeat(auto-fill,
      minmax(300px, 1fr));
  gap: 16px;
}
/* Items always have
   equal widths
   on every row */

Verdict: Grid wins here. With Flexbox, items on the last row might stretch to fill space unevenly (e.g., two items filling a three-column width). Grid's auto-fill + minmax produces consistently even columns on every row.

Comparison 3: Overlapping Elements

Flexbox

/* Not possible with Flexbox.
   You need position: absolute
   or negative margins,
   which breaks the flex
   flow and is fragile. */

Grid

.stack {
  display: grid;
}
.stack > * {
  grid-area: 1 / 1;
  /* All children occupy
     the same cell,
     stacking naturally */
}
.overlay {
  align-self: end;
  padding: 16px;
  background: rgba(0,0,0,0.7);
}

Verdict: Grid wins decisively. Overlapping elements (like image overlays, stacked cards, or hero sections with text on top of an image) are natural with Grid and hacky with Flexbox.

Comparison 4: Item Reordering

Flexbox

.container {
  display: flex;
}
.item:nth-child(1) {
  order: 2;
}
.item:nth-child(2) {
  order: 1;
}
/* Can reorder within
   the flex flow */

Grid

.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.item:nth-child(1) {
  grid-column: 2;
  grid-row: 1;
}
.item:nth-child(2) {
  grid-column: 1;
  grid-row: 1;
}
/* Can place anywhere */

Verdict: Flexbox's order property is simpler for one-dimensional reordering. Grid allows placing items anywhere on the grid, which is more powerful but more verbose for simple cases.

Using Grid and Flexbox Together

The real power comes from combining both. Here is a production pattern that most modern websites use:

html
<div class="page">
  <header class="header">
    <nav class="navbar">...</nav>
  </header>
  <main class="content">
    <div class="card-grid">
      <article class="card">...</article>
      <article class="card">...</article>
      <article class="card">...</article>
    </div>
  </main>
  <footer class="footer">...</footer>
</div>
css
/* GRID: Page structure */
.page {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

/* FLEXBOX: Navbar items */
.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  height: 64px;
}

/* GRID: Card layout */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 24px;
  padding: 24px;
}

/* FLEXBOX: Card internals */
.card {
  display: flex;
  flex-direction: column;
  background: #111118;
  border-radius: 12px;
  overflow: hidden;
}

.card-body {
  flex: 1;
  padding: 20px;
}

.card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  border-top: 1px solid #1e1e2e;
}

/* FLEXBOX: Footer links */
.footer {
  display: flex;
  justify-content: center;
  gap: 24px;
  padding: 32px;
}

Notice the pattern: Grid handles the two-dimensional structure (page layout, card grid), while Flexbox handles one-dimensional alignment (navbar, card content, footer links). This is the mental model that will serve you for every layout you build.

Real-World Layout Patterns

Pattern 1: Holy Grail Layout

The classic header, left sidebar, main content, right sidebar, and footer layout:

css
.holy-grail {
  display: grid;
  grid-template:
    "header  header  header"  auto
    "left    main    right"   1fr
    "footer  footer  footer"  auto
    / 200px  1fr     200px;
  min-height: 100vh;
  gap: 0;
}

.header  { grid-area: header; }
.left    { grid-area: left; }
.main    { grid-area: main; }
.right   { grid-area: right; }
.footer  { grid-area: footer; }

@media (max-width: 768px) {
  .holy-grail {
    grid-template:
      "header" auto
      "main"   1fr
      "footer" auto
      / 1fr;
  }
  .left, .right { display: none; }
}

Pattern 2: Masonry-Like Layout

While true CSS masonry is still behind a flag in most browsers as of early 2026, you can approximate it with Grid:

css
/* Approximate masonry with column-count (works today) */
.masonry {
  column-count: 3;
  column-gap: 16px;
}

.masonry-item {
  break-inside: avoid;
  margin-bottom: 16px;
}

@media (max-width: 768px) {
  .masonry { column-count: 2; }
}

@media (max-width: 480px) {
  .masonry { column-count: 1; }
}

/* True CSS Masonry (when available) */
@supports (grid-template-rows: masonry) {
  .masonry {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: masonry;
    gap: 16px;
    column-count: initial; /* Override fallback */
  }

  .masonry-item {
    break-inside: initial;
    margin-bottom: 0;
  }
}

Pattern 3: Responsive Feature Section

css
.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 32px;
  padding: 64px 24px;
}

.feature {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.feature-icon {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(99, 102, 241, 0.1);
  border-radius: 12px;
  font-size: 1.5rem;
}

.feature h3 {
  font-size: 1.125rem;
  font-weight: 600;
}

.feature p {
  color: #94a3b8;
  line-height: 1.6;
}

Pattern 4: Pricing Table

css
.pricing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
  align-items: start;
  max-width: 1000px;
  margin: 0 auto;
}

.pricing-card {
  display: flex;
  flex-direction: column;
  border: 1px solid #1e1e2e;
  border-radius: 16px;
  padding: 32px;
}

.pricing-card.featured {
  border-color: #6366f1;
  position: relative;
}

.pricing-card .price {
  font-size: 3rem;
  font-weight: 800;
  margin: 16px 0;
}

.pricing-card .features-list {
  flex: 1;
  list-style: none;
  padding: 0;
  margin: 24px 0;
}

.pricing-card .features-list li {
  padding: 8px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.pricing-card .cta {
  margin-top: auto; /* Pushed to bottom by flex */
  width: 100%;
}

CSS Subgrid: Aligning Nested Elements

Subgrid solves one of the most frustrating layout problems: aligning content across cards when content lengths vary. Without subgrid, card titles, descriptions, and buttons misalign when content has different lengths. With subgrid, nested elements participate in the parent grid's tracks.

css
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
}

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 4; /* Card occupies 4 parent rows: image, title, desc, button */
  gap: 0;
  border: 1px solid #1e1e2e;
  border-radius: 12px;
  overflow: hidden;
}

/* Now, across all cards in a row:
   - All images are the same height
   - All titles align on the same baseline
   - All descriptions start at the same position
   - All buttons sit at the same level */

.card img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card h3 {
  padding: 16px 16px 0;
  align-self: start;
}

.card p {
  padding: 8px 16px;
  color: #94a3b8;
}

.card .btn {
  margin: 16px;
  align-self: end;
}
Browser Support

CSS subgrid has full support in Chrome, Firefox, Safari, and Edge since 2024. You can use it in production today without fallbacks for modern audiences.

Subgrid for Form Alignment

Subgrid is also perfect for aligning form fields in a grid, where each field group has a label and input that should align with neighboring groups:

css
.form {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px 16px;
}

.field-group {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
}

/* All labels share the same column width,
   determined by the widest label.
   All inputs share the remaining space. */

Container Queries: The Missing Piece

Container queries are the most significant CSS feature to reach full browser support in recent years. They allow you to style elements based on the size of their parent container instead of the viewport.

Why Container Queries Matter

With media queries, a card component always responds to the viewport width. But what if that card appears in a narrow sidebar on desktop? Media queries see a wide viewport and render the desktop layout — even though the card only has 280px of space. Container queries solve this.

css
/* Define a containment context */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Style based on container width */
.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* When the card's container is at least 500px wide,
   switch to horizontal layout */
@container card (min-width: 500px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card-image {
    width: 200px;
    flex-shrink: 0;
  }

  .card-body {
    flex: 1;
  }
}

/* When the card's container is narrow (sidebar),
   show a compact version */
@container card (max-width: 300px) {
  .card-image {
    display: none;
  }

  .card-body h3 {
    font-size: 0.9rem;
  }
}

Container Query Units

Container queries also introduce new units that are relative to the container's size:

css
.card-container {
  container-type: inline-size;
}

.card h3 {
  /* Font size scales with container width */
  font-size: clamp(1rem, 3cqi, 1.5rem);
  /* cqi = 1% of container inline size */
}

.card p {
  font-size: clamp(0.8rem, 2cqi, 1rem);
}

/* Available container units:
   cqw — container query width (1%)
   cqh — container query height (1%)
   cqi — container query inline size (1%)
   cqb — container query block size (1%)
   cqmin — smaller of cqi/cqb
   cqmax — larger of cqi/cqb */

Modern CSS Techniques (2026)

Logical Properties

Logical properties make your layouts work correctly in all writing directions (LTR, RTL) without separate stylesheets:

css
/* Instead of: */
.box {
  margin-left: 16px;
  margin-right: 16px;
  padding-top: 12px;
  padding-bottom: 12px;
  border-left: 3px solid #6366f1;
}

/* Use logical properties: */
.box {
  margin-inline: 16px;      /* Start and end (left/right in LTR) */
  padding-block: 12px;      /* Start and end (top/bottom in LTR) */
  border-inline-start: 3px solid #6366f1; /* Left in LTR, right in RTL */
}

The :has() Selector

The :has() selector (the "parent selector") enables layout changes based on child content:

css
/* Card layout changes based on whether it has an image */
.card:has(img) {
  grid-template-columns: 200px 1fr;
}

.card:not(:has(img)) {
  grid-template-columns: 1fr;
}

/* Form field styling based on input state */
.field:has(input:invalid) {
  border-color: #ef4444;
}

.field:has(input:focus) {
  border-color: #6366f1;
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}

/* Navbar changes when it contains a search input */
.navbar:has(.search-input:focus) {
  background: rgba(0, 0, 0, 0.95);
}

Scroll-Driven Animations

A newer CSS feature that eliminates the need for JavaScript scroll event handlers:

css
.fade-in {
  animation: fadeIn linear;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Progress bar that tracks scroll position */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: #6366f1;
  transform-origin: left;
  animation: scaleProgress linear;
  animation-timeline: scroll();
}

@keyframes scaleProgress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

Performance Considerations

Both Grid and Flexbox are highly optimized in modern browsers, but there are a few things to keep in mind:

Layout Recalculation

Content-Based vs. Container-Based Sizing

Practical Impact

For most applications, the performance difference is negligible. You will only notice it with thousands of elements (e.g., large data tables or infinite-scroll feeds). In those cases, prefer Grid for the outer structure and minimize nested flex containers.

Performance Tip

Avoid deeply nested flex containers (flex inside flex inside flex). Each level adds a layout calculation pass. Flatten your structure where possible, or use Grid to skip a level of nesting.

The Decision Framework

When you sit down to build a layout, use this decision framework:

Question If Yes
Is the layout a single row or column of items? Flexbox
Do items need to align in both rows AND columns? Grid
Should the content determine the layout (intrinsic)? Flexbox
Should the container determine the layout (extrinsic)? Grid
Do elements need to overlap? Grid
Is it a navigation bar or toolbar? Flexbox
Is it a card grid or dashboard? Grid
Is it alignment within a component? Flexbox
Is it the overall page structure? Grid

When in doubt, start with Grid for the outer layout and Flexbox for the inner components. You can always refactor later — switching between Grid and Flexbox usually requires changing only 2-3 lines of CSS.

Build CSS Layouts Visually

Use NexTool's free visual tools to generate Grid and Flexbox CSS without memorizing every property.

CSS Grid Generator Flexbox Playground

CSS Layout Tools

Visual tools accelerate your workflow when prototyping layouts. Here are NexTool's free CSS tools:


Frequently Asked Questions

Use Flexbox for one-dimensional layouts (a row OR a column) where items need to distribute space along a single axis. Use CSS Grid for two-dimensional layouts (rows AND columns) where you need precise control over placement in both directions. In practice, most modern layouts use both: Grid for the overall page structure and Flexbox for component-level alignment within grid cells.

Absolutely, and you should. CSS Grid and Flexbox are complementary, not competing technologies. A common pattern is to use Grid for the macro layout (page sections, card grids, dashboard panels) and Flexbox for the micro layout (navigation items, button groups, card content alignment). Grid cells can contain flex containers, and vice versa.

CSS subgrid allows a grid item's children to participate in the parent grid's track sizing. This means nested elements can align to the same grid lines as their parent's siblings. Use subgrid when you need consistent alignment across card layouts — for example, aligning titles, descriptions, and buttons across a row of cards regardless of content length. Subgrid has full browser support as of 2024.

Container queries let you style elements based on the size of their parent container, not the viewport. While media queries respond to the browser window width (useful for page-level layout), container queries respond to the component's available space (useful for reusable components). This means a card component can adapt its layout whether it appears in a narrow sidebar or a wide main content area, without any JavaScript.

NT

NexTool Team

We build developer tools and write in-depth guides on modern web development. Our free CSS tools are used by thousands of developers to build better layouts faster.

Need a Custom Website Built?

NexTool builds beautiful, responsive websites with modern CSS. Describe what you need, and we build it — starting at $5.

Get a Free Quote