Flexbox fundamentals — one-dimensional layout

Flexbox solves exactly one problem — distributing space among items along a single axis — and nearly every confusing flexbox behavior traces back to the difference between the main axis and the cross axis.

Beginner

3 min read

The one problem flexbox actually solves

.toolbar {
  display: flex;
  justify-content: space-between; /* spread items along the MAIN axis */
  align-items: center;            /* center items along the CROSS axis */
}

Flexbox exists to answer one question well: given a row (or column) of items and some available space, how should that space be distributed among them? It's deliberately one-dimensional — it reasons about a single axis at a time, which is exactly what makes it the right tool for a toolbar, a nav bar, or a row of buttons, and the wrong tool for a full page grid with both rows and columns that need to align together (that's the next lesson's job).

The main axis and cross axis: the single idea that unlocks flexbox

.row { display: flex; flex-direction: row; }    /* main axis: horizontal, cross axis: vertical */
.col { display: flex; flex-direction: column; }  /* main axis: vertical, cross axis: horizontal */

Every flex property aligns items along one of exactly two axes, and which physical direction each axis points depends entirely on flex-direction. justify-content always controls the main axis (the direction items flow in); align-items always controls the cross axis (perpendicular to that). This is the single fact that resolves most flexbox confusion: justify-content: center centers horizontally in a row but vertically in a column — not because the property changed meaning, but because the main axis itself rotated 90 degrees.

flex-grow, flex-shrink, flex-basis: how extra (or missing) space gets divided

.sidebar { flex: 0 0 250px; } /* grow: 0 (never grow), shrink: 0 (never shrink), basis: 250px (starting size) */
.main    { flex: 1 1 auto;  } /* grow: 1 (take all extra space), shrink: 1, basis: auto */

flex-basis sets an item's starting size before any extra/missing space gets distributed. flex-grow is a ratio, not a fixed amount: an item with flex-grow: 1 next to one with flex-grow: 2 doesn't get "1 unit" of space — it gets exactly half as much of the leftover space as its neighbor, however much leftover space there actually is. flex-shrink works the same way in reverse, for when items don't all fit. The shorthand flex: 1 (equivalent to flex: 1 1 0%) is the common "take up all available space, splitting evenly with siblings that also say flex: 1" pattern — a sidebar-plus-main-content layout, exactly as shown above, is one of flexbox's most common real uses.

gap replaced margin-juggling between flex children

/* Old way — margins on every child except the last, easy to get subtly wrong */
.item:not(:last-child) { margin-right: 16px; }
 
/* Modern way — one line on the flex container itself */
.toolbar { display: flex; gap: 16px; }

Before gap was supported inside flex containers, spacing items evenly required margin tricks that had to specifically exclude the last child (otherwise there'd be unwanted trailing space) — a small but real source of off-by-one styling bugs. gap sets spacing between items directly on the flex container, with no such exception needed, and it's now supported in every current browser, making the old margin-exclusion pattern effectively obsolete for new code.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What one problem does flexbox exist to solve?

2. Why does `justify-content: center` center horizontally in a row but vertically in a column?

3. What does `flex-grow: 1` on an item next to a sibling with `flex-grow: 2` actually mean?