CSS Flexbox Explained: The Complete Guide to the Flex Layout Model, Alignment, and Gaps
2026-08-17
You set display: flex on a container and three boxes line up in a row. Then justify-content: center works, align-items: center does something you didn’t expect, a long word blows the whole layout apart, and your items mysteriously shrink even though you never asked them to. Flexbox is the most-used layout mode in CSS and the most misunderstood, because it ships with a mental model — axes — that nothing before it had. Once that model clicks, the entire property list becomes obvious. This guide builds it up from first principles.
The Two Axes: Main and Cross
Every flex layout has two axes, and every flex property is defined against one of them. That single fact clears up most confusion:
- Main axis — the direction the items flow along. Controlled by
flex-direction. - Cross axis — the perpendicular direction.
flex-direction: row (the default) makes the main axis horizontal — items sit in a line left to right — and the cross axis vertical. flex-direction: column swaps them: items stack top to bottom, and the cross axis is horizontal.
The consequences:
justify-contentdistributes items along the main axis.align-itemspositions items along the cross axis.
This is the classic swap: people expect align-items: center to center horizontally because it contains “align.” It centers vertically in a row layout. If you ever find a property “not working,” ask first: which axis is it acting on, and is that the axis I mean?
The Container and Its Items
display: flex turns the element into a flex container, and its direct children become flex items. Two things follow:
- Only direct children are flex items. A grandchild stays a normal block unless its own parent is also a flex container — which is why real layouts nest flex containers.
- The container and the items have disjoint property sets. Container properties (
flex-direction,flex-wrap,justify-content,align-items,align-content,gap) control arrangement; item properties (flex-grow,flex-shrink,flex-basis,align-self,order,margin) control the item’s own sizing and position.
The Main Axis: Direction, Distribution, Wrap, Gap
Direction. flex-direction accepts row, row-reverse, column, column-reverse. row and column are the two you’ll use; the -reverse variants mirror the start/end points (useful for Arabic layouts and careful visual ordering).
Distribution. justify-content decides what happens to the free space along the main axis:
flex-start/flex-end— items crowd toward the start or end edge (the default isflex-start).center— grouped in the middle.space-between— no space at the ends, equal space between items.space-around— equal space on both sides of every item (the ends get half a gap).space-evenly— equal space everywhere, including the ends.
Wrap. By default flex-wrap: nowrap forces every item onto one line, which means items either shrink or overflow. flex-wrap: wrap lets items flow onto additional lines when they run out of room — the essential switch for responsive rows of cards or badges.
Gap. gap (with row-gap and column-gap for each axis) sets spacing between items. It replaces the old margin-and-nth-child hacks and, unlike margins, never double-applies at the edges.
The Cross Axis: align-items, align-content, align-self
align-items positions items along the cross axis. Its surprising default is stretch: a flex item stretches to fill the container’s cross size unless its own width/height (or align-self) says otherwise. That’s why the classic mistake — align-items: center on a container whose items don’t center — often shows up as items that do center but only after you account for stretch. Other values: flex-start, flex-end, center, and baseline (aligns the text baselines of items of different heights — useful for rows of buttons or form labels).
align-content looks like a sibling but acts only when items wrap onto multiple lines. It distributes the lines themselves along the cross axis. If you have one line of items, it does nothing — that’s a common source of “why doesn’t this center?” confusion.
align-self overrides align-items for a single item — handy when one element in a row should hang at the bottom while the rest center.
Sizing the Items: flex-grow, flex-shrink, flex-basis
Three item properties decide how big each item actually is on the main axis:
flex-basis— the item’s starting main size before any growing or shrinking.auto(the default) uses the item’swidth/heightor content size.0means “start from nothing, size me entirely from grow shares.”flex-grow— if there is leftover space after all items take their basis, it’s distributed among items in proportion to their grow values. Default0(don’t grow).flex-shrink— if the total basis exceeds the container, items shrink in proportion to their shrink values. Default1.
The default flex-shrink: 1 is the reason items “shrink when you didn’t ask them to”: any flex container with fixed-width children that overflow will quietly compress them. Two ways to stop it: flex-shrink: 0, or the shorthand flex: none.
The shorthand bundles all three: flex: 1 means 1 1 0% (grow equally, shrink allowed, ignore the basis), flex: auto means 1 1 auto (grow equally but respect content size), flex: none means 0 0 auto (never grow or shrink). flex: 2 grows twice as fast as flex: 1 siblings.
One more gotcha: a flex item refuses to shrink below its min-content size (min-width: auto) unless you set min-width: 0. A long unbroken word or a huge image inside a flex item will therefore overflow the container no matter what you shrink — the fix is min-width: 0 (or overflow: hidden) on the item.
The Classic Mistakes
justify-contentvsalign-itemsswapped. Ask which axis you’re on. In arow, horizontal isjustify-contentand vertical isalign-items; in acolumn, both flip.- “Vertical centering doesn’t work.” You’re centering along the cross axis of a row — use
align-items: center, notjustify-content: center. - “Why is my item shrinking?”
flex-shrink: 1is the default. Pinflex-shrink: 0or useflex: none. - “My text overflows and I can’t fix it.”
min-width: autoprevents shrinking below content width; setmin-width: 0on the offending item. - Margins for spacing. Use
gap; it’s the tool built for it. - The centering one-liner.
margin: autoon a flex item absorbs all free space, which is why a single item withmargin: autocenters itself on both axes — the classic “perfectly center a button” trick with no flex properties at all.
Building Real Layouts: Nesting Flex
Flex shines in small compositions. A nav header is a container with justify-content: space-between (logo left, links right) plus align-items: center and a gap between links. A toolbar is a row of items with align-items: center. A card with an icon and two lines of text is a column flex container with gap between them. And for a two-column “sidebar + content” page shell, you can let the content column flex: 1 so the sidebar keeps its width and the main area absorbs the rest.
For anything where the layout must align in two dimensions at once — a full page skeleton, a grid of cards that lines up rows and columns — flexbox becomes awkward. That’s the job of CSS Grid: grid defines rows and columns up front, while flexbox lays out one axis at a time. A common architecture uses grid for the page skeleton and flexbox inside each cell for the fine-grained alignment, and the two compose beautifully.
Quick Reference
flex-directionsets the main axis (row = horizontal, column = vertical); the cross axis is perpendicular.justify-contentworks on the main axis;align-itemson the cross axis.align-items: stretchis the default — items fill the cross size unless told otherwise.flex-wrap: wrap+gap= responsive rows without margin hacks.flex-basis= starting size,flex-grow= share of leftover space,flex-shrink= how fast it compresses.flex: 1=1 1 0%,flex: none=0 0 auto.- Items shrink by default (
flex-shrink: 1) and refuse to shrink below content size (min-width: auto) — pin either one when it bites. - One axis at a time → flexbox. Two dimensions → grid.
If you’d rather design a flex layout visually, the CSS Flexbox Generator lets you drag alignment and sizing options and copies the finished CSS — and the CSS Grid Generator does the same for grid.