Container, items & the two axes
Flexbox has two players: a flex container (the parent you put display: flex on) and its flex items (the direct children). Everything you do is either a container property or an item property.
The key mental model is the two axes. The main axis runs in the direction items flow (a row, by default). The cross axis runs perpendicular to it. justify-content works along the main axis; align-items works along the cross axis.
flex-direction to column and the two axes swap.Turning it on: display: flex
A plain div full of children stacks them vertically. Add one line to the parent and they snap into a row:
.row { display: flex; } children now sit side by side
flex-direction: row or column
flex-direction sets the main axis. row (default) flows left-to-right; column flows top-to-bottom. Click to flip it:
column mode, justify-content now controls vertical spacing and align-items controls horizontal — because the main axis turned vertical.justify-content (main axis)
This distributes items along the main axis — packing them or spreading the free space between them. Click each value:
space-between is the navbar classic (logo left, links right). center centers a group. The three space-* values differ only in the edge gaps.align-items (cross axis)
This aligns items across the main axis — vertically, in a row. The container is taller here so you can see it work. Click each value:
justify-content: center + align-items: center — dead center on both axes, the answer to the web's oldest question.gap: spacing without margins
Old flexbox layouts used margins on every child (and fiddly “remove the last margin” hacks). gap replaces all that — one property, even spacing between items, none on the edges.
.row { display: flex; gap: 16px; }
gap as your default for spacing flex (and grid) children. It's cleaner than margins and never leaves a stray gap at the start or end.flex-wrap: let items break to new lines
By default flex items refuse to wrap — they squish to fit one line. flex-wrap: wrap lets them flow onto new lines when they run out of room, which is essential for responsive rows of cards. Toggle it (six cards that overflow one line):
flex-flow: row wrap sets direction and wrap together. A wrapping flex row is the simplest responsive card layout you can write.Sizing items: grow, shrink, basis
Those were container properties. Now the items: how do they share space? The flex shorthand on each item bundles three things:
| Part | Means |
|---|---|
| flex-grow | How much of the leftover space this item takes (0 = none). |
| flex-shrink | How willingly it shrinks when space is tight (1 = yes). |
| flex-basis | Its starting size before grow/shrink (often auto or a width). |
Click to change how item 2 grows relative to its siblings:
.sidebar { flex: 0 0 240px; } don't grow/shrink, fixed 240px .main { flex: 1; } take all remaining space
flex: 1 on every item makes them equal width and fill the row — the quickest way to split a row into equal columns. Mix a fixed item with a flex: 1 one to get a sidebar + flexible main.Five patterns you'll use constantly
Navbar: logo left, links right
.nav { display: flex; justify-content: space-between; align-items: center; }
Perfect centering
.center { display: flex; justify-content: center; align-items: center; min-height: 100vh; }
Sidebar + flexible main
.layout { display: flex; gap: 24px; } .layout .side { flex: 0 0 240px; } .layout .main { flex: 1; }
Equal-width columns
.cols { display: flex; gap: 16px; } .cols > * { flex: 1; }
Push one item to the end
.bar .last { margin-left: auto; } auto margin eats the free space
Flexbox cheat sheet
-
display: flexgoes on the parent; sizing props go on the items -
justify-content= main axis ·align-items= cross axis -
flex-direction: columnswaps which axis is which - Space items with
gap, not margins - Add
flex-wrap: wrapso rows reflow on small screens -
flex: 1= equal, growing items ·flex: 0 0 240px= fixed - Use Flexbox for one dimension; reach for Grid when you need rows and columns