← All posts

CSS Grid vs. Flexbox: When to Use Which

If you’re like a lot of designers who write CSS, Flexbox is probably your default. It’s flexible, it’s forgiving, and it solves 80% of layout problems without much thought. But CSS Grid isn’t a competitor trying to replace Flexbox — it’s a different tool solving a different problem. Once the distinction clicks, you’ll stop reaching for Flexbox out of habit and start picking the right tool on purpose.

The one-sentence version

Flexbox is one-dimensional. Grid is two-dimensional.

Flexbox lays things out along a single axis — a row or a column — and lets content determine sizing. Grid lays things out along rows and columns at the same time, and lets you define the structure up front, independent of content.

That’s really the whole difference. Everything else is a consequence of it.

Flexbox: content-first layout

Flexbox asks: “I have a row (or column) of items — how should they distribute themselves?” It’s great when your layout is really a sequence of items whose sizes should respond to their content or to available space.

.nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

Classic Flexbox jobs:

  • A navbar with a logo on the left and links on the right
  • A row of buttons that should share space evenly
  • Centering a single item vertically and horizontally
  • A card’s internal layout (icon + text + button, all in a row)
  • Wrapping tags/chips that flow to a new line as needed

Notice the theme: these are all one row or one column of things. Even when Flexbox wraps, it’s still just repeating that one-dimensional logic — each new line doesn’t know or care what’s above it.

Grid: structure-first layout

Grid asks the opposite question: “Here’s a structure — rows and columns — now place items into it.” You define the skeleton first, then decide where things go, independent of their content.

.page {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "sidebar header"
    "sidebar main"
    "sidebar footer";
  gap: 1.5rem;
  min-height: 100vh;
}

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

Classic Grid jobs:

  • Full page layouts (header, sidebar, main, footer)
  • Card grids/galleries where rows and columns need to align
  • Dashboards with items of different sizes on a shared grid
  • Anything where an item in row 2 needs to line up with something in row 5
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1rem;
}

That one line — repeat(auto-fill, minmax(200px, 1fr)) — is doing something Flexbox genuinely can’t: creating a responsive number of equal, aligned columns without a single media query.

Why “just use Flexbox for everything” breaks down

Try building a photo gallery with Flexbox and flex-wrap, and you’ll notice items in different rows don’t line up in columns — because Flexbox has no concept of a column once it wraps. Every row is its own independent flex line. Grid tracks the whole matrix, so column 2 in row 1 is always aligned with column 2 in row 3.

This is the tell: if you find yourself fighting to make things line up across multiple rows, that’s Grid’s job, not Flexbox’s.

They work great together

In practice, most real interfaces use both — Grid for the page skeleton, Flexbox for the components living inside it.

/* Grid for the overall page structure */
.page {
  display: grid;
  grid-template-columns: 250px 1fr;
}

/* Flexbox for a component inside a grid cell */
.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

Think of Grid as the floor plan and Flexbox as the furniture arrangement within each room.

A quick decision guide

Ask yourselfUse
Is this basically a single row or column of items?Flexbox
Do items need to size based on their content?Flexbox
Do I need things to align across multiple rows and columns?Grid
Am I laying out the overall page/section structure?Grid
Do I want equal-width items that wrap responsively?Grid (auto-fill/auto-fit)
Am I centering one or two things?Flexbox

The mental shortcut

  • Flexbox = “I have some items, distribute them along a line.”
  • Grid = “I have a layout, place items into cells.”

A quick mention on Tailwind:

Tailwind doesn’t change the Grid-vs-Flexbox decision itself, but it does change a few practical things about how you reach for each one.

1. Tailwind makes Flexbox the “cheaper” choice by default

flex, items-center, justify-between, gap-4 are short, memorable, and you’ll type them constantly. Grid utilities (grid-cols-12, col-span-4, grid-rows-[auto_1fr_auto]) require more setup. This nudges people toward defaulting to Flexbox even when Grid is the better fit — worth resisting.

2. Arbitrary values make Grid’s real power usable in Tailwind

Grid’s killer feature — repeat(auto-fill, minmax(200px, 1fr)) — has no shorthand utility. You write it as an arbitrary value:

<div class="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-4">

Same with named template areas — you basically always drop to arbitrary values:

<div class="grid grid-cols-[250px_1fr] grid-rows-[auto_1fr_auto] 
            grid-areas-[sidebar_header,sidebar_main,sidebar_footer]">

(In practice, most people skip grid-template-areas in Tailwind entirely and just use col-start/col-span/row-start/row-span instead — it maps more naturally to utility classes.)

3. Responsive prefixes make “switch layout at a breakpoint” trivial

This is where Tailwind genuinely earns its keep for this topic — flipping between Flexbox and Grid, or reconfiguring a grid, per breakpoint is just prefixes:

<div class="flex flex-col gap-4 md:grid md:grid-cols-3">

or resizing a grid responsively without media queries at all:

<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">

4. gap unifies the two

One nice thing: Tailwind’s gap-* utilities work identically whether you’re on flex or grid, so you don’t have to think differently about spacing between the two — that mental overhead disappears.

5. Nesting still follows the same rule as vanilla CSS

Grid for the shell, Flex for the component, same as before — just with classes instead of separate rule blocks:

<div class="grid grid-cols-[250px_1fr] min-h-screen">
  <aside class="p-4">...</aside>
  <main class="flex flex-col gap-4 p-6">...</main>
</div>

More to read

See all

Comments

Loading comments…

Not published. Required for moderation.