Skip to content
All posts
Tutorial8 min read

CSS Grid vs Flexbox: When to Use Each (With Real Examples)

J
Jamith Nimantha
April 19, 2026
Clean web design layout on a computer screen
Photo by Unsplash
On this page

Flexbox lays out one dimension, Grid lays out two. If you are arranging items along a single line, a row or a column, that is Flexbox. If rows and columns have to line up with each other at the same time, that is Grid.

They are not competitors and you will use both in the same page, usually Grid for the page skeleton and Flexbox inside each region. The rest of this covers where the rule bites, the two bugs everyone hits, and what changed with subgrid.

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

css
.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

css
.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:

css
.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: auto in 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

css
.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

css
.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:

css
.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

text
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

css
/* ❌ 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:

css
/* ✅ Do this */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
}

Mistake 2: Using Grid for simple alignment

css
/* ❌ 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:

css
/* 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.

Mistake 4: auto-fill when you meant auto-fit

These two look interchangeable and behave completely differently when the content does not fill the row. It is the most-searched Grid confusion for a reason.

css
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
grid-template-columns: repeat(auto-fit,  minmax(280px, 1fr));

With auto-fill, the browser creates as many tracks as fit, and leaves the empty ones in place. Three cards in a container wide enough for five give you three cards at 280px and two columns of empty space on the right.

With auto-fit, the empty tracks are collapsed to zero width, so the 1fr on the remaining tracks lets the three cards stretch to fill the row.

Neither is correct in general. Use auto-fit when you want items to expand to fill the space. Use auto-fill when you want a stable grid rhythm and items that keep their size regardless of how many there are.

Mistake 5: The flex item that refuses to shrink

The single most reported Flexbox bug, and it is not a bug. A long string or a <pre> block inside a flex item overflows its container instead of wrapping or scrolling:

css
.sidebar-layout { display: flex; }
.content { flex: 1; }          /* still overflows */

The cause is that flex items default to min-width: auto, which means "at least as wide as my content". An unbreakable string therefore sets a floor the item cannot shrink below. Override it:

css
.content {
  flex: 1;
  min-width: 0;      /* now it can shrink, and overflow-x works */
}

The same applies to min-height: 0 in a column layout, which is why a scrollable panel inside flex-direction: column so often refuses to scroll. Grid items have the same behaviour, where the fix is minmax(0, 1fr) instead of 1fr.

Modern CSS That Changes Things

In 2026, container queries change the equation slightly:

css
.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;
    }
}

subgrid closes the oldest gap

Grid's long-standing weakness was that a nested grid could not line up with its parent. Card titles at different lengths meant card bodies started at different heights, and the usual workaround was fixed heights or a taller wrapper.

subgrid fixes it properly. The child adopts the parent's track sizing instead of defining its own:

css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  grid-template-rows: auto auto auto;
}

.card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;   /* title, body and footer align across cards */
}

Every card's title, body and footer now sit on the same rows as its neighbours, with no fixed heights. It is supported across current versions of all major browsers, so check your own support target before shipping it.

But the fundamental rule still holds: Grid for 2D structure, Flexbox for 1D alignment.

The alignment properties mean different things in each

Both layouts share the same property names and apply them to different axes, which is why alignment is where people lose the most time.

PropertyIn FlexboxIn Grid
justify-contentdistributes items along the main axisdistributes the whole column track set along the inline axis
align-itemsaligns items across the cross axisaligns items within their row
justify-itemsno effectaligns items within their column
place-itemsno effectshorthand for align-items + justify-items
align-selfone item, cross axisone item, within its row

Two consequences worth remembering. In Flexbox, justify-content follows flex-direction, so switching to column swaps which axis it controls, and that catch alone accounts for a lot of "why did my centering break on mobile". In Grid, the axes are fixed, so justify-* is always inline and align-* is always block, regardless of anything else.

Centering one element is where they finally agree. Both of these work:

css
.center { display: flex; align-items: center; justify-content: center; }
.center { display: grid; place-items: center; }

The Grid version is one line, which is a reasonable argument for reaching for it even on a single item.

Quick Reference

Use CaseWinner
Navbar with logo + links + buttonFlexbox
Responsive card gridGrid
Centering a modalEither (Flexbox is simpler)
Full-page app layoutGrid
Card with pinned footer buttonFlexbox
Overlapping hero text on imageGrid
Form with label + input pairsGrid
Tag/badge list that wrapsFlexbox
Dashboard with sidebar + mainGrid
Button with icon + textFlexbox

Tools in this post

Related Tool

Image Compressor

Compress, resize, and convert JPEG, PNG, and WebP images directly in your browser. Optimize image file size without losing quality.

Try it free
J

Written by

Jamith Nimantha

Software developer. Builds the DebuggerMe tools and writes about the things he runs into shipping them.

Share this post

Back to all posts

Related Articles

All articles →