Flexbox
Flexbox lays out children in one dimension — a row or a column — and distributes free space among them. It is the right tool for toolbars, navbars, button groups, card footers, centring, and anything where “these items sit in a line and share the space”.
For two-dimensional layout, where rows and columns must align, use Grid.
The model
Section titled “The model”.container { display: flex; }That single declaration does three things: the container becomes a block-level box, its direct children become flex items, and those items lay out along the main axis.
- Main axis — the direction items flow. Set by
flex-direction. Horizontal by default. - Cross axis — perpendicular to the main axis.
- Main start / main end, cross start / cross end — the edges. These flip with
flex-directionand with writing direction, which is why the properties are namedjustify-*(main) andalign-*(cross) rather than “horizontal” and “vertical”.
Everything else follows from that: justify-content distributes along the main axis, align-items along the cross axis. Change flex-direction and the two swap meaning.
Only direct children become flex items. Text nodes directly inside the container become anonymous flex items too.
Container properties
Section titled “Container properties”flex-direction
Section titled “flex-direction”.c { flex-direction: row; } /* default: left→right (in LTR) */.c { flex-direction: row-reverse; } /* right→left */.c { flex-direction: column; } /* top→bottom */.c { flex-direction: column-reverse; } /* bottom→top */row follows the writing direction, so in an RTL document row runs right to left automatically.
flex-wrap and flex-flow
Section titled “flex-wrap and flex-flow”.c { flex-wrap: nowrap; } /* default: items shrink rather than wrap */.c { flex-wrap: wrap; }.c { flex-wrap: wrap-reverse; } /* new lines stack towards cross-start */
.c { flex-flow: row wrap; } /* shorthand: direction + wrap */nowrap is the default and is why a row of items squashes instead of moving to a second line.
justify-content — main axis
Section titled “justify-content — main axis”.c { justify-content: flex-start; } /* default */.c { justify-content: flex-end; }.c { justify-content: center; }.c { justify-content: space-between; } /* first at start, last at end, equal gaps between */.c { justify-content: space-around; } /* equal space around each item: edges get half */.c { justify-content: space-evenly; } /* every gap equal, including the edges */justify-content only has an effect when there is free space on the main axis. If items fill the line (or overflow it), nothing moves.
align-items — cross axis, per line
Section titled “align-items — cross axis, per line”.c { align-items: stretch; } /* default: items fill the cross size */.c { align-items: flex-start; }.c { align-items: flex-end; }.c { align-items: center; }.c { align-items: baseline; } /* align the items' first text baselines */stretch is why flex items in a row all come out the same height by default — they stretch to the tallest one. It only applies if the item’s cross size is auto (no explicit height in a row layout).
baseline is the right choice when items contain text at different sizes and you want the text to line up rather than the boxes.
align-content — cross axis, between lines
Section titled “align-content — cross axis, between lines”Only meaningful when the container wraps into multiple lines and has extra cross-axis space:
.c { flex-wrap: wrap; height: 400px; align-content: space-between; /* also: flex-start|flex-end|center|space-around|stretch */}.c { gap: 1rem; } /* both axes */.c { gap: 1rem 2rem; } /* row-gap column-gap */.c { column-gap: 2rem; }gap in flexbox is supported in every current browser (Chrome 84+, Firefox 63+, Safari 14.1+). It replaces the old margin + negative-margin-on-parent hack entirely, and unlike margins it does not add space outside the first and last items.
Aligning the container’s own text
Section titled “Aligning the container’s own text”The place-content / place-items shorthands work here too:
.c { place-items: center; } /* align-items + justify-items (justify-items is ignored in flex) */.c { place-content: center; } /* align-content + justify-content */Note justify-items has no effect in flexbox — there is no per-item main-axis alignment property on the container. Use margin: auto on the item instead.
Item properties
Section titled “Item properties”flex-grow
Section titled “flex-grow”How much of the positive free space this item absorbs, as a proportion of all items’ grow factors.
.a { flex-grow: 1; }.b { flex-grow: 2; } /* takes twice as much of the leftover space as .a */flex-grow: 2 does not make an item twice as wide as its sibling. It takes twice as much of the remaining space after every item’s base size has been laid out. If both items start at 100px and there is 300px free, .a ends at 200px and .b at 300px.
Default: 0 (do not grow).
flex-shrink
Section titled “flex-shrink”How much of the negative free space (overflow) this item gives up. Default: 1, which is why flex items shrink below their width by default.
.logo { flex-shrink: 0; } /* never shrink this */Shrinking is weighted by the item’s base size as well as its shrink factor, so a wide item shrinks more than a narrow one at the same factor.
flex-basis
Section titled “flex-basis”The item’s size along the main axis before growing or shrinking.
.item { flex-basis: 200px; }.item { flex-basis: 30%; } /* percentage of the container's main size */.item { flex-basis: auto; } /* default: use width/height, else content size */.item { flex-basis: content; }/* size from content, ignoring width */When flex-basis is anything other than auto, it overrides width (in a row) or height (in a column).
The flex shorthand
Section titled “The flex shorthand”Always use the shorthand. It sets sensible defaults for the parts you leave out, and the longhand defaults are different from the shorthand defaults.
.item { flex: 1; } /* = 1 1 0% grow, shrink, ignore content size */.item { flex: auto; } /* = 1 1 auto grow, shrink, start from content size */.item { flex: none; } /* = 0 0 auto fixed at content size */.item { flex: initial; } /* = 0 1 auto the default: shrink only */.item { flex: 0 0 240px; } /* fixed 240px track */.item { flex: 2 1 300px; } /* grow twice as fast, shrink normally, base 300px */The difference between flex: 1 and flex: auto is the single most useful thing to internalise:
flex: 1→ basis0. Content size is ignored; all items end up equal width regardless of content.flex: auto→ basisauto. Content size is the starting point; items with more content end up wider.
<div class="row"> <div class="col">Short</div> <div class="col">Much much longer content here</div></div>.row { display: flex; gap: 1rem; }.col { flex: 1; } /* two equal halves *//* .col { flex: auto; } -> the second column is wider */align-self
Section titled “align-self”Overrides align-items for one item.
.container { align-items: center; }.container .last { align-self: flex-end; }Changes the visual order. Default 0; items sort by order, then by source order. Negative values are allowed.
.sidebar { order: -1; } /* move it first without changing the HTML */margin: auto
Section titled “margin: auto”An auto margin absorbs all free space on that side, before justify-content gets a look in. This is the flexbox way to push one item away from the rest:
.nav { display: flex; gap: 1rem; }.nav .login { margin-inline-start: auto; } /* pinned to the far end */Common layouts
Section titled “Common layouts”Perfect centring
Section titled “Perfect centring”.center { display: flex; justify-content: center; /* main axis */ align-items: center; /* cross axis */ min-height: 100dvh;}Navbar: brand left, links right
Section titled “Navbar: brand left, links right”<nav class="nav"> <a class="brand" href="/">Acme</a> <ul class="links"> <li><a href="/docs">Docs</a></li> <li><a href="/pricing">Pricing</a></li> </ul></nav>.nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem 1.5rem;}.links { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; }Sidebar + fluid main
Section titled “Sidebar + fluid main”.layout { display: flex; gap: 2rem; }.sidebar { flex: 0 0 260px; } /* fixed, never grows or shrinks */.main { flex: 1; min-width: 0; } /* takes the rest — see the min-width note below */Sticky footer
Section titled “Sticky footer”body { min-height: 100dvh; display: flex; flex-direction: column; }main { flex: 1; } /* main absorbs all leftover height, pushing the footer down */Media object
Section titled “Media object”.media { display: flex; gap: 1rem; align-items: flex-start; }.media img { flex: none; inline-size: 64px; block-size: 64px; border-radius: 50%; }.media .body { flex: 1; min-width: 0; }Responsive wrapping card row
Section titled “Responsive wrapping card row”.cards { display: flex; flex-wrap: wrap; gap: 1rem; }.card { flex: 1 1 280px; } /* at least 280px, then share leftover space */This gives a responsive grid with no media queries: cards sit side by side until they cannot be 280px wide, then wrap.
Gotchas
Section titled “Gotchas”min-width: auto prevents shrinking
Section titled “min-width: auto prevents shrinking”Flex items have an automatic minimum size: they will not shrink smaller than their content. That is why a long unbreakable string or a nested overflowing element blows out the layout, and why overflow: hidden / text-overflow: ellipsis on a flex child often does nothing.
.child { flex: 1; min-width: 0; /* the fix, in a row layout */}.column-child { min-height: 0; /* the fix, in a column layout */}overflow: hidden on the item also works, because a non-visible overflow changes the automatic minimum size to zero.
Percentages in flex-basis need a definite container size
Section titled “Percentages in flex-basis need a definite container size”flex-basis: 50% resolves against the container’s main size. In a column layout with no explicit height, that percentage may resolve to auto.
align-items: stretch fights explicit heights
Section titled “align-items: stretch fights explicit heights”If an item has height: 200px, stretch cannot apply to it. Similarly align-items: center collapses an item to its content height — which is why a centred flex item sometimes looks smaller than you expect.
Nested flex containers need display: flex again
Section titled “Nested flex containers need display: flex again”A flex item is not automatically a flex container. Set display: flex on it too if its own children need flex layout.
Absolutely positioned children are not flex items
Section titled “Absolutely positioned children are not flex items”position: absolute removes a child from flex layout. It is positioned against the container (if the container is position: relative) using its static position, and justify-content/align-items still influence that static position — but it takes up no space.
gap vs justify-content: space-between
Section titled “gap vs justify-content: space-between”They compose: gap sets a minimum space, space-between distributes what remains. With gap: 1rem; justify-content: space-between, gaps will be at least 1rem and larger when there is free space.
Buttons and flex-shrink
Section titled “Buttons and flex-shrink”Text inputs have an intrinsic default width (around 20 characters) and will shrink below it. Give inputs min-width: 0 and let the wrapper control width, or set flex: 1.
Flexbox vs Grid, briefly
Section titled “Flexbox vs Grid, briefly”Reach for flexbox when the content should determine the layout — a row of items of unknown count and unknown width. Reach for grid when the layout should determine where content goes — a defined set of rows and columns you place things into. Full comparison in Grid.
Key points
Section titled “Key points”justify-*is the main axis,align-*is the cross axis;flex-directionswaps which is which.flex: 1means basis0(equal columns);flex: automeans basisauto(content-sized columns).flex-growdistributes leftover space, not total width.align-contentneedsflex-wrap: wrapto do anything.margin-inline-start: autopushes one item to the far end.- Flex items refuse to shrink below their content unless you set
min-width: 0. orderbreaks keyboard order — reorder the DOM instead for interactive content.