HandyTools Hub

← All guides

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-content distributes items along the main axis.
  • align-items positions 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 is flex-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’s width/height or content size. 0 means “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. Default 0 (don’t grow).
  • flex-shrink — if the total basis exceeds the container, items shrink in proportion to their shrink values. Default 1.

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-content vs align-items swapped. Ask which axis you’re on. In a row, horizontal is justify-content and vertical is align-items; in a column, both flip.
  • “Vertical centering doesn’t work.” You’re centering along the cross axis of a row — use align-items: center, not justify-content: center.
  • “Why is my item shrinking?” flex-shrink: 1 is the default. Pin flex-shrink: 0 or use flex: none.
  • “My text overflows and I can’t fix it.” min-width: auto prevents shrinking below content width; set min-width: 0 on the offending item.
  • Margins for spacing. Use gap; it’s the tool built for it.
  • The centering one-liner. margin: auto on a flex item absorbs all free space, which is why a single item with margin: auto centers 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-direction sets the main axis (row = horizontal, column = vertical); the cross axis is perpendicular.
  • justify-content works on the main axis; align-items on the cross axis.
  • align-items: stretch is 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.