DEV Community

mfaysal dev
mfaysal dev

Posted on Originally published at mfaysal.com

CSS Flexbox vs Grid: When to Use Each

The short answer to CSS Flexbox vs Grid: use Flexbox when you're lining things up in one direction, a row or a column, and want the content to decide the sizes. Use Grid when you need rows and columns at the same time, or when the layout should decide where things go. Most real pages use both: Grid for the overall layout and card lists, Flexbox for the small pieces inside them. Below I show how I decide, with code you can copy, plus the mistakes I made while learning both.

I used to pick one at random and fight it until the layout worked. Once I understood the "one dimension vs two dimensions" idea, choosing became easy. Every snippet here passes a CSS validator, and I rendered the layouts in headless Chrome to check they look the way I describe.

The core difference: one dimension vs two

Flexbox works along one main axis. Items flow in a line, and you control how they share the space, how they line up and whether they wrap. Wrapped lines don't line up with each other, though, because each line is laid out on its own.

Grid works on two axes at once. You define columns and rows on the container, and items are placed into that structure. Every item in a column lines up with the items above and below it.

Another way I think about it:

  • Flexbox is content-out. The size of the items drives the layout.
  • Grid is layout-in. You draw the structure first, then put items into it.

When to use Flexbox

Navigation bars and toolbars

A header with a logo on the left and links on the right is a single row, which is the perfect Flexbox job:

<header class="site-header">
  <a class="logo" href="/">mfaysal.dev</a>
  <nav aria-label="Main">
    <ul class="nav-links">
      <li><a href="/blog">Blog</a></li>
      <li><a href="/projects">Projects</a></li>
      <li><a href="/contact">Contact</a></li>
    </ul>
  </nav>
</header>
Enter fullscreen mode Exit fullscreen mode
.site-header {
  display: flex;
  align-items: center;            /* vertical centring on the cross axis */
  justify-content: space-between; /* logo left, links right */
  gap: 1rem;
  padding: 1rem 1.5rem;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
Enter fullscreen mode Exit fullscreen mode

justify-content controls spacing along the main axis (horizontal here), and align-items controls the cross axis (vertical here). gap adds space between items without margin hacks. It's been supported for Flexbox in all major browsers for years now.

One item takes the leftover space

A search input that stretches next to a fixed-size button is another classic Flexbox pattern:

<form class="search-bar" action="/search">
  <input name="q" type="search" placeholder="Search posts" aria-label="Search posts">
  <button type="submit">Search</button>
</form>
Enter fullscreen mode Exit fullscreen mode
.search-bar {
  display: flex;
  gap: 0.5rem;
}

.search-bar input {
  flex: 1;        /* take all the leftover space */
  min-width: 0;   /* allow it to shrink below its default width */
}
Enter fullscreen mode Exit fullscreen mode

flex: 1 tells the input to grow into whatever space is left. The min-width: 0 line fixes a very common bug: flex items won't shrink smaller than their content by default, so without it an input can overflow on small screens.

When to use CSS Grid

Responsive card grids without media queries

This is the Grid snippet I use most. It fits as many columns as there's room for, each at least 220px wide:

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1.5rem;
}
Enter fullscreen mode Exit fullscreen mode
<div class="card-grid">
  <article class="card">
    <h2>Quizwright</h2>
    <p>AI quiz generator.</p>
    <a href="/projects">View</a>
  </article>
  <article class="card">
    <h2>StudyPilot</h2>
    <p>Offline study planner with a longer description that wraps onto more lines.</p>
    <a href="/projects">View</a>
  </article>
  <article class="card">
    <h2>Kinetica</h2>
    <p>Canvas design and animation editor.</p>
    <a href="/projects">View</a>
  </article>
</div>
Enter fullscreen mode Exit fullscreen mode

On a wide screen you get several columns; on a phone, one. No media queries needed. There's a small difference between the two keywords: auto-fill keeps empty column tracks when there are only a few cards, so they stay card-sized. auto-fit collapses the empty tracks, so the existing cards stretch to fill the row. I usually prefer auto-fill, because three cards stretched across a huge monitor look odd.

Whole-page layouts with grid-template-areas

For a header, sidebar, main content and footer, named areas make the CSS read almost like a picture of the layout:

<div class="page">
  <header>Header</header>
  <aside>Sidebar</aside>
  <main>Main content</main>
  <footer>Footer</footer>
</div>
Enter fullscreen mode Exit fullscreen mode
.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 1rem;
}

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

@media (max-width: 700px) {
  .page {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto auto;
    grid-template-areas:
      "header"
      "main"
      "sidebar"
      "footer";
  }
}
Enter fullscreen mode Exit fullscreen mode

On small screens, the media query switches to one column and moves the sidebar below the main content just by changing the area map. The HTML order stays the same, which matters for keyboard and screen reader users.

Dashboards with items of different sizes

When some boxes should be wider or taller than others, Grid's span keyword is exactly right:

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

.widget--wide { grid-column: span 2; }
.widget--tall { grid-row: span 2; }
.widget--hero { grid-column: span 4; }
Enter fullscreen mode Exit fullscreen mode

When I rendered this, a "wide" widget took two of the four columns, a "tall" one took two rows, and the small widgets filled the gaps in order. My offline study planner, StudyPilot, has a dashboard where each widget can be 1 to 4 columns wide and 1 to 3 rows tall. That kind of layout is a two-dimensional problem, which is Grid territory. Flexbox can't make an item span two rows.

Using Flexbox and Grid together

The best layouts often nest them. In the card grid above, Grid places the cards, and each card uses Flexbox inside to keep its "View" link at the bottom, even when descriptions have different lengths:

.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.25rem;
  border: 1px solid #2c3550;
  border-radius: 12px;
}

.card a {
  margin-top: auto; /* pushes the link to the bottom of every card */
}
Enter fullscreen mode Exit fullscreen mode

margin-top: auto on a flex item eats up all the free space above it, so the link sits at the bottom of every card. Because Grid makes all cards in a row the same height by default, the links line up across the row too.

Centring: both work

Centring something in a box is no longer hard. Both of these work:

/* Flexbox */
.center-flex {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 300px;
}

/* Grid: one line does both axes */
.center-grid {
  display: grid;
  place-items: center;
  min-height: 300px;
}
Enter fullscreen mode Exit fullscreen mode

I use the Grid version when I just want something centred, because place-items: center is one line. I use the Flexbox version when there are several items in a row that should be centred together.

Flexbox vs Grid: quick decision checklist

  • Items in a single row or column, sizes based on content: Flexbox.
  • Need items to line up in both rows and columns: Grid.
  • Need an item to span several rows: Grid.
  • Nav bars, button groups, tags, form rows, the inside of cards: Flexbox.
  • Page layouts, card galleries, dashboards, image galleries: Grid.
  • Not sure? Start with the one that needs less code, and switch if you find yourself fighting it.

Common mistakes

1. Using flex-wrap and expecting a grid

With flex-wrap: wrap, the last row often ends up with stretched or oddly sized items, because every flex line is sized separately. If you want neat columns, that's a sign you need Grid.

2. Forgetting min-width: 0

Flex and grid items have an automatic minimum size based on their content. Long words, URLs or inputs can then overflow the container. min-width: 0 on the item (or minmax(0, 1fr) for grid columns) lets them shrink.

3. Putting display on the wrong element

display: flex and display: grid go on the parent, and only affect its direct children. Grandchildren aren't laid out by it, unless you also make the child a flex or grid container (or use subgrid).

4. Confusing justify and align

In Flexbox, justify-content follows the main axis, which changes when you set flex-direction: column. If your centring suddenly works "the wrong way", check the direction.

5. Reordering content only visually

order and grid placement change where things appear, not the order a keyboard or screen reader follows. Keep the HTML in a logical order.

FAQ

Is CSS Grid better than Flexbox?

Neither is better. They solve different problems. Grid is for two-dimensional layouts, Flexbox is for one-dimensional ones, and they work well together.

Can I use Grid for everything?

You could build almost any layout with Grid, but simple rows like a nav bar or a button group take less code with Flexbox, and Flexbox handles items of unknown width more naturally.

What is subgrid?

grid-template-columns: subgrid (or rows) lets a nested grid reuse its parent's tracks, so content inside different cards can line up with each other. It's supported in all current major browsers.

Do I still need media queries?

Less often. Patterns like repeat(auto-fill, minmax(220px, 1fr)) and flex-wrap adapt on their own, but bigger changes, like moving a sidebar below the content, still use media queries or container queries.

Conclusion

Flexbox vs Grid isn't a competition. Reach for Flexbox for things in a line, Grid for things in rows and columns, and combine them freely. Grid for the page and the card list, Flexbox inside each card, is a pattern you'll use constantly.

If you want to go deeper, MDN has clear guides on basic concepts of grid layout and how it relates to Flexbox.

Originally published at mfaysal.com.

Top comments (0)