CSS Flexbox in Practice —
Patterns You'll Actually Use Every Day

Flexbox isn't hard — it's just poorly explained most of the time. This guide walks through the concepts that actually matter in real projects, with live demos you can interact with right here on the page.

What Flexbox Actually Solves

Before Flexbox became widely supported around 2015, laying out elements horizontally meant wrestling with float, clearfix hacks, and display: inline-block quirks. I spent years calculating pixel-perfect margins only to have them break when content length changed. If you've been in the field long enough, you know the pain.

Flexbox — formally the CSS Flexible Box Layout Module — was designed to solve one-dimensional layout. "One-dimensional" means it handles alignment along a single axis at a time: either a row or a column. That sounds simple, and it is. The key insight is that Flexbox lets the parent container dictate how its children are sized and spaced, rather than making each child figure it out on its own.

Container and Items — the Two Roles

Every Flexbox layout has two participants: the flex container (the parent) and the flex items (its direct children). The moment you write display: flex on a parent element, all its direct children become flex items automatically — no extra class names needed.

The simplest Flexbox layout
<div class="container">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
</div>

.container {
  display: flex;
}

That's it. Three children, side by side, no floats, no clearfix, no margin calculations. The simplicity is the whole point.

▶ Live Demo — display: flex in action
A
B
C
💡 Pro Tip
The gap property on flex containers has been fully supported in all modern browsers since 2021. Before that, we used margins. If you're still adding margin-right to flex items and removing it from the last child — you can stop now.

Main Axis and Cross Axis — the Mental Model That Unlocks Everything

The single most important concept in Flexbox is understanding the two axes. By default, the main axis runs horizontally (left to right), and the cross axis runs vertically (top to bottom). justify-content always controls alignment along the main axis, while align-items controls alignment along the cross axis.

Here's where people get tripped up: when you switch to flex-direction: column, the axes swap. Suddenly justify-content controls vertical alignment, and align-items controls horizontal alignment. The property names don't change — the axis does.

Switching axes with flex-direction
/* Default: horizontal main axis */
.row {
  display: flex;
  flex-direction: row;
}

/* Vertical main axis */
.column {
  display: flex;
  flex-direction: column;
}
▶ Live Demo — flex-direction: row vs column

flex-direction: row

1
2
3

flex-direction: column

1
2
3

The Holy Grail: Perfect Centering in Three Lines

For years, vertically centering an element in CSS was a running joke in the community. We used position: absolute with negative margins, table-cell hacks, and all sorts of creative workarounds. With Flexbox, it takes exactly three lines.

Perfect centering — horizontal and vertical
.center-box {
  display: flex;
  justify-content: center;  /* main axis (horizontal) */
  align-items: center;      /* cross axis (vertical) */
}
▶ Live Demo — Perfect centering
Perfectly Centered
📌 Key Point
For vertical centering to work, the parent needs an explicit height. Without it, the container collapses to the height of its content, leaving no room for vertical alignment. In real projects, I usually combine this with min-height: 100vh or min-height: 100dvh for full-screen hero sections.

justify-content — Distributing Space Along the Main Axis

justify-content is the property you'll reach for most often. It determines how flex items are distributed along the main axis, and the difference between its values becomes immediately obvious when you see them side by side.

The key justify-content values
.container {
  display: flex;
  justify-content: flex-start;    /* packed to the start (default) */
  justify-content: center;        /* centered */
  justify-content: flex-end;      /* packed to the end */
  justify-content: space-between; /* first and last flush to edges */
  justify-content: space-around;  /* equal space around each item */
  justify-content: space-evenly; /* truly equal gaps everywhere */
}
▶ Live Demo — justify-content comparison
flex-start
center
space-between
space-evenly

In my experience, space-between is the most frequently used value in production code. Navbars with a logo on the left and links on the right, card footers with a date on one side and a button on the other, pricing rows with the label and the price — it all comes down to space-between.

flex-wrap and Responsive Card Grids

By default, flex items try to squeeze into a single line — that's because flex-wrap defaults to nowrap. For card layouts and any scenario where items should flow onto the next line, you need flex-wrap: wrap.

❌ The fragile way — items get crushed
.card-grid {
  display: flex;
  gap: 16px;
  /* no flex-wrap — items shrink to fit */
}
✅ The robust way — natural wrapping
.card-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.card {
  flex: 1 1 280px;  /* grow, shrink, min 280px */
}

The magic is in flex: 1 1 280px. It tells each card: "You can grow, you can shrink, but start from 280px." When the row can't fit another 280px card, it wraps naturally. This gives you a responsive grid with zero media queries — it's one of my favorite Flexbox patterns.

▶ Live Demo — Responsive card grid with flex-wrap
🎨
Design
Core UI design principles
💻
Code
Clean HTML/CSS implementation
📱
Responsive
Works on every device
Performance
Fast and lightweight

Try resizing the browser — cards wrap naturally

The Classic Navbar Pattern

One of the places Flexbox shines brightest is navigation bars. The classic logo-on-the-left, links-on-the-right pattern used to require floats and a clearfix. Now it's a single space-between declaration.

Navbar — logo left, links right
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 24px;
  height: 64px;
}

.nav-links {
  display: flex;
  gap: 24px;
  list-style: none;
}
▶ Live Demo — Navigation bar

Understanding flex-grow, flex-shrink, and flex-basis

These three properties — often written as the flex shorthand — are where Flexbox gets its real power, and where most confusion lives. Let's break them down clearly.

flex-grow controls how much extra space an item absorbs. flex-shrink controls how much an item gives up when space is tight. flex-basis sets the starting size before growing or shrinking happens. When you write flex: 1, you're actually saying flex: 1 1 0% — grow equally, shrink equally, start from zero.

Reading the flex shorthand
/* flex: [grow] [shrink] [basis] */

.item { flex: 0 1 auto; }  /* default: don't grow, can shrink, natural size */
.item { flex: 1; }          /* = 1 1 0%  — absorb extra space equally */
.item { flex: none; }       /* = 0 0 auto — rigid, won't budge */
.item { flex: 1 1 200px; } /* start at 200px, then flex */
▶ Live Demo — flex-grow visualized
All items flex: 1 (equal width)
flex: 1
flex: 1
flex: 1
First item flex: 2 (takes double)
flex: 2
flex: 1
flex: 1

The Sidebar + Main Content Pattern

One of the most practical applications of flex-grow is the sidebar layout. Give the sidebar flex: none with a fixed width, and give the main area flex: 1. The main content fills whatever space is left — no calculations needed.

▶ Live Demo — Sidebar + main content
Sidebar
120px fixed
Main Content (takes all remaining space)
⚠️ Watch Out
flex: 1 means flex: 1 1 0% — the basis is 0%, not auto. This means space is divided without considering content size. flex: auto (which equals 1 1 auto) distributes space after accounting for content size. The difference matters when your items have varying amounts of text.

Takeaways

  • Flexbox handles one-dimensional layout by letting the parent container control how children are sized, spaced, and aligned — replacing the old float-and-clearfix approach entirely.
  • Understanding main axis vs. cross axis is the key: justify-content always works along the main axis, align-items along the cross axis, regardless of flex-direction.
  • Perfect vertical and horizontal centering takes exactly three CSS properties: display: flex, justify-content: center, and align-items: center.
  • Combine flex-wrap: wrap with flex: 1 1 [min-width] on children to create responsive card grids without a single media query.
  • The flex shorthand (grow, shrink, basis) is the heart of flexible sizing — learn the difference between flex: 1, flex: auto, and flex: none.
  • Use align-self and order for per-item overrides, but be mindful of accessibility — visual order should generally match DOM order for screen readers and keyboard navigation.